/*  OpenOlap viewer
 *  パッケージ名：openolap.viewer.export
 *  ファイル：ExportReportAsXMLSpreadsheetSchema.java
 *  説明：XML Spreadsheet Schema形式でレポートをエクスポートするクラスです。
 *
 *  作成日: 2004/01/31
 */
package openolap.viewer.export;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Hashtable;

import javax.servlet.http.HttpServletRequest;


import openolap.viewer.Axis;
import openolap.viewer.AxisMember;
import openolap.viewer.CellData;
import openolap.viewer.Col;
import openolap.viewer.Measure;
import openolap.viewer.MeasureMember;
import openolap.viewer.Report;
import openolap.viewer.Row;
import openolap.viewer.common.Constants;
import openolap.viewer.common.Messages;
import openolap.viewer.common.StringUtil;
import openolap.viewer.controller.RequestHelper;
import openolap.viewer.dao.CellDataDAO;
import openolap.viewer.dao.DAOFactory;
import openolap.viewer.manager.CellDataManager;

/**
 *  クラス：ExportReportAsXMLSpreadsheetSchema<br>
 *  説明：XML Spreadsheet Schema形式でレポートをエクスポートするクラスです。
 */
public class ExportReportAsXMLSpreadsheetSchema extends ExportReport {

	// ********** インスタンス変数 **********

	/** xIndex変換用テーブル
	 *  (クライアントのSpreadIndex⇒エクスポートされるレポート構造をもとに新たに振りなおしたIndex) 
     */
	private Hashtable xIndexChangeTable = null;

	/** yIndex変換用テーブル 
	 *  (クライアントのSpreadIndex⇒エクスポートされるレポート構造をもとに新たに振りなおしたIndex) 
	 */
	private Hashtable yIndexChangeTable = null;

	/**
	 * エクスポート処理を実行し、ダウンロードページのパスを戻す。
	 * @param helper RequestHelperオブジェクト
	 * @return dispatch先のJSP/HTMLのパス
	 */
	public String exportReport(RequestHelper helper) {

		int i = 0;
		int j = 0;
		int k = 0;

		HttpServletRequest request = helper.getRequest();
		Report report = (Report) helper.getRequest().getSession().getAttribute("report");
		if (report==null) {
			throw new IllegalStateException();
		}

		// ファイルパス、ファイルURLを設定
		String dirPath = helper.getConfig().getServletContext().getRealPath("/") + "export";
		String fileName = "xmlSpreadsheet" + request.getSession().getId() + ".xml";
		String filePath = dirPath + "/" + fileName;

		String fileURL = request.getContextPath() + "/" + Messages.getString("ExportReportAsCSV.exportTmpDir") + "/" + fileName;

		helper.getRequest().setAttribute("downloadURL", fileURL);

		DAOFactory daoFactory = DAOFactory.getDAOFactory();
		Connection conn = null;
		conn = daoFactory.getConnection((String)helper.getRequest().getSession().getAttribute("connectionPoolName"),
										(String)helper.getRequest().getSession().getAttribute("searchPathName"));


		FileOutputStream fs = null;
		OutputStreamWriter osw = null;
		PrintWriter out = null;
		try {

			// =============== 共通情報を取得 ===============

			// 行・列に配置された全DimensionオブジェクトにDimensionMemberオブジェクトをセット
			report.setSelectedCOLROWDimensionMembers(helper, report, conn);

			// セルデータ(値は単位フォーマットのみ)を取得
			ArrayList cellDataList = CellDataManager.selectCellDatas(helper, conn, false, CellDataDAO.normalSQLTypeString);
			TreeMap dataRowMap = CellDataManager.getCellDataTable(report, cellDataList);	// セルデータをテーブル形式にソートし直したもの

			// Index変換テーブル(Key:oldSpreadIndex, value:newSpreadIndex)
			xIndexChangeTable = getSpreadIndexChangeTable(dataRowMap,"x");
			yIndexChangeTable = getSpreadIndexChangeTable(dataRowMap,"y");

			// エッジ
			Col col = (Col) report.getEdgeByType(Constants.Col);
			Row row = (Row) report.getEdgeByType(Constants.Row);
			
			// エッジの持つ軸リスト			
			ArrayList colAxesList = col.getAxisList();
			ArrayList rowAxesList = row.getAxisList();
			ArrayList pageAxesList = report.getEdgeByType(Constants.Page).getAxisList();

			// 軸が持つメンバー数
			ArrayList colAxisMembersList = new ArrayList();
			ArrayList rowAxisMembersList = new ArrayList();

			// 色情報
			TreeMap colColorMap = this.getColorMap(helper, Constants.Col);
			TreeMap rowColorMap = this.getColorMap(helper, Constants.Row);
			TreeMap dataColorMap = this.getColorMap(helper, Constants.Data);

			// 色とスタイルIDの対応表を作成
			Hashtable colColorStyleTable = getColorStyleList("s" + Constants.Col, colColorMap);	
			Hashtable rowColorStyleTable = getColorStyleList("s" + Constants.Row, rowColorMap);
			Hashtable dataColorStyleTable = getColorStyleList("s" + Constants.Data, dataColorMap);

			// 色情報をオブジェクトに保存
			TableColorInfo colTableColorInfo = new TableColorInfo(("s" + Constants.Col), colColorMap, colColorStyleTable);
			TableColorInfo rowTableColorInfo = new TableColorInfo(("s" + Constants.Row), rowColorMap, rowColorStyleTable);
			TableColorInfo dataTableColorInfo = new TableColorInfo(("s" + Constants.Data), dataColorMap, dataColorStyleTable);


			String colIndexKeysString = (String)request.getSession().getAttribute("viewColIndexKey_hidden");
			String rowIndexKeysString = (String)request.getSession().getAttribute("viewRowIndexKey_hidden");

			// =============== XML Spreadsheet ファイル生成処理開始 ===============

			fs = new FileOutputStream(filePath, false);	//既存のファイルがある場合、上書きする
			osw = new OutputStreamWriter(fs,"UTF-8");
			out = new PrintWriter(new BufferedWriter(osw));

			// =============== XML Spreadsheet 出力開始 ===============
			out.println("<?xml version=\"1.0\"?>");
			out.println("<?mso-application progid=\"Excel.Sheet\"?>");
			out.println("<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"");
			out.println("xmlns:o=\"urn:schemas-microsoft-com:office:office\"");
			out.println("xmlns:x=\"urn:schemas-microsoft-com:office:excel\"");
			out.println("xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"");
			out.println("xmlns:html=\"http://www.w3.org/TR/REC-html40\">");


			// ===== スタイル =====

			out.println("<Styles>");
				out.println("<Style ss:ID=\"border\">");
					out.println("<Borders>");
					out.println("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("</Borders>");
				out.println("</Style>");
				out.println("<Style ss:ID=\"header\">");
					out.println("<Borders>");
					out.println("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("</Borders>");
					out.println("<Interior ss:Color=\"#CCFFFF\" ss:Pattern=\"Solid\"/>");
				out.println("</Style>");
				out.println("<Style ss:ID=\"sCOL\">");
					out.println("<Borders>");
					out.println("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("</Borders>");
					out.println("<Interior ss:Color=\"#99CCFF\" ss:Pattern=\"Solid\"/>");
				out.println("</Style>");
				out.println("<Style ss:ID=\"sROW\">");
					out.println("<Borders>");
					out.println("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("</Borders>");
					out.println("<Interior ss:Color=\"#99CCFF\" ss:Pattern=\"Solid\"/>");
				out.println("</Style>");
				out.println("<Style ss:ID=\"sDATA\">");
					out.println("<Borders>");
					out.println("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
					out.println("</Borders>");
				out.println("</Style>");

				// === 色づけされたセルのスタイルを出力 ===

				// 行・列のセルスタイルを出力
				// 書式（色設定の無いセル）：sCOL<colorIndex>
				// 書式（色設定のあるセル）：sROW<colorIndex>
				this.printColorStyle(colColorStyleTable, out);
				this.printColorStyle(rowColorStyleTable, out);

				// データテーブル部のセルのスタイルを出力。
				// 戻り値：メジャーメンバータイプのgroupNameをKeyに持ち、重複するgroupNameを排除し新たに0から振りなおしたインデックス(memberTypeIndex)をValueに持つHashMap
				// 		  indexはスタイルIDの一部
				// 書式（色設定の無いセル)：sDATA_<memberTypeIndex>)
				// 書式（色設定のあるセル)：sDATA<colorIndex>_<memberTypeIndex>)
				HashMap measureMemberTypeStyleIndexMap = this.printColorAndFormatStyle(dataColorStyleTable, report, out);

			out.println("</Styles>");

			// ===== ワークシート =====
			out.println("<Worksheet ss:Name=\"Sheet1\">");
			out.println("<Table>");

			// レポートタイトル
			this.printlnRowStartTag(out);
			this.printlnCell(report.getReportName(), "border", 0, 0, 0, "String", out);
			this.printlnRowEndTag(out);

			// ページエッジ軸名
			this.printlnRowStartTag(out);
			this.printlnCell("", "", 0, 0, 0, "String", out);

			Iterator pageIt = pageAxesList.iterator();
			while (pageIt.hasNext()) {
				Axis axis = (Axis) pageIt.next();
				this.printlnCell(axis.getName(), "border", 0, 0, 0, "String", out);
			}
			this.printlnRowEndTag(out);

			// ページエッジデフォルトメンバー名
			this.printlnRowStartTag(out);
			this.printlnCell("選択メンバー", "border", 0, 0, 0, "String", out);

			pageIt = pageAxesList.iterator();
			while (pageIt.hasNext()) {
				Axis axis = (Axis) pageIt.next();
				this.printlnCell(axis.getDefaultMemberName(conn), "border", 0, 0, 0, "String", out);
			}
			this.printlnRowEndTag(out);

			// 空白行を出力
			this.printlnRowStartTag(out);
			this.printlnCell("", "", 0, 0, 0, "String", out);
			this.printlnRowEndTag(out);

			// 列ヘッダの軸名リスト
			this.printlnRowStartTag(out);
			for (i = 0; i < rowAxesList.size(); i++) {
				this.printlnCell("","", 0, 0, 0, "String", out);
			}
			Iterator colIt = colAxesList.iterator();
			while (colIt.hasNext()) {
				Axis axis = (Axis) colIt.next();
				this.printlnCell(axis.getName(),"header", 0, 0, 0, "String", out);
			}
			this.printlnRowEndTag(out);

			// ===== クロスヘッダ部、列ヘッダ部出力 =====
			colIt = colAxesList.iterator();
			i = 0;
			while (colIt.hasNext()) {

				// 行出力の開始タグ
				this.printlnRowStartTag(out);
				Axis colAxis = (Axis) colIt.next();

				// ===== クロスヘッダ部 =====
				if (i == (colAxesList.size()-1)) {	// 最終段の場合、軸タイトルを表示

					// 行ヘッダの軸名リスト
					Iterator rowIt = rowAxesList.iterator();
					while (rowIt.hasNext()) {
						Axis rowAxis = (Axis) rowIt.next();
						this.printlnCell(rowAxis.getName(),"header", 0, 0, 0, "String", out);
					}

				} else {

					// 列ヘッダ表示位置を整えるため、空白セルを出力
					for (j = 0; j < rowAxesList.size(); j++) {
						this.printlnCell("","header", 0, 0, 0, "String", out);
					}

				}

				// ===== 列ヘッダ部(メンバー名を出力) =====

				// SpreadIndexの順でソート
				TreeMap colIndexKeyMap = new TreeMap();
				ArrayList colIndexKeyList = StringUtil.splitString(colIndexKeysString, ",");
				Iterator colIndexKeyIt = colIndexKeyList.iterator();

				while (colIndexKeyIt.hasNext()) {
					String colIndexKey = (String) colIndexKeyIt.next();
					ArrayList colIndexKeys = StringUtil.splitString(colIndexKey, ":");
					String index = (String) colIndexKeys.get(0);
					Integer ind  = Integer.decode(index);
					String keys = (String) colIndexKeys.get(1);
					colIndexKeyMap.put(ind, keys);
				}

				Iterator colIndexKeyMapIt = colIndexKeyMap.keySet().iterator();

				String beforeKey = null;
				String beforeKeys = null;
				int mergeNum = 0;
				int beforeJ  = 0;
				j = 0;
				while (colIndexKeyMapIt.hasNext()) {

					Integer index = (Integer) colIndexKeyMapIt.next();
					String keys = (String) colIndexKeyMap.get(index);

					ArrayList keyList = StringUtil.splitString(keys, ";");
					String key = (String) keyList.get(i);

					if (isJoinMember(beforeKeys, keys, i)) {
						mergeNum++;
					} else {

						if ( j != 0 ) {
							String styleID = getCellStyle(colTableColorInfo, beforeJ, i);
							this.printlnCell(colAxis.getAxisMemberByUniqueName(beforeKey).getSpecifiedDisplayName(colAxis), styleID, 0, mergeNum, 0, "String", out);
						}

						beforeJ = j;
						beforeKey = key;
						beforeKeys = keys;
						mergeNum = 0;
					}

					j++;
				}

				// 最後の要素を表示する
				String styleID = getCellStyle(colTableColorInfo, beforeJ, i);
				this.printlnCell(colAxis.getAxisMemberByUniqueName(beforeKey).getSpecifiedDisplayName(colAxis), styleID, 0, mergeNum, 0, "String", out);

				// 行出力の終了タグ
				this.printlnRowEndTag(out);
				i++;
		  	}
	
		
			// ===== 行ヘッダ部(メンバー名)、データセル部(値)出力 =====

			// SpreadIndexの順でソート
			TreeMap rowIndexKeyMap = new TreeMap();
			ArrayList rowIndexKeyList = StringUtil.splitString(rowIndexKeysString, ",");
			Iterator rowIndexKeyIt = rowIndexKeyList.iterator();

			while (rowIndexKeyIt.hasNext()) {
				String rowIndexKey = (String) rowIndexKeyIt.next();
				ArrayList rowIndexKeys = StringUtil.splitString(rowIndexKey, ":");
				String index = (String) rowIndexKeys.get(0);
				Integer ind  = Integer.decode(index);
				String keys = (String) rowIndexKeys.get(1);
				rowIndexKeyMap.put(ind, rowIndexKey);
			}

			// ArrayListに持ち直し
			Iterator rowIndexKeyMapIt = rowIndexKeyMap.keySet().iterator();
			ArrayList sortedIndexKeyList = new ArrayList();
			while (rowIndexKeyMapIt.hasNext()) {
				Integer ind = (Integer) rowIndexKeyMapIt.next();
				sortedIndexKeyList.add( rowIndexKeyMap.get(ind));
			}


			Iterator rowIndexIt = sortedIndexKeyList.iterator();
			String beforeKeys = null;
			int rowIndex = 0;
			while (rowIndexIt.hasNext()) {
				String rowIndexKey = (String) rowIndexIt.next();
				ArrayList rowIndexKeys = StringUtil.splitString(rowIndexKey, ":");
				String index = (String) rowIndexKeys.get(0);
				Integer ind  = Integer.decode(index);
				String keys = (String) rowIndexKeys.get(1);
				ArrayList keyList = StringUtil.splitString(keys, ";");

				this.printlnRowStartTag(out);


				// 行ヘッダー出力
				for (i = 0; i < rowAxesList.size(); i++ ){

					if (!this.isJoinMember(beforeKeys, keys, i)) { 
						Axis rowAxis = (Axis) rowAxesList.get(i);
						String key = (String) keyList.get(i);
						AxisMember axisMember = (AxisMember) rowAxis.getAxisMemberByUniqueName(key);

						int rowCellMergeNum = this.getFollowingSameKeyNums(rowIndex, i, sortedIndexKeyList);

						String styleID = getCellStyle(rowTableColorInfo, i, rowIndex);
						this.printlnCell(axisMember.getSpecifiedDisplayName((Axis)rowAxesList.get(i)), styleID, (i+1), 0, rowCellMergeNum, "String", out);

					}

				}

				// データテーブル部出力
				this.printDataRow(dataRowMap, dataTableColorInfo, report.getMeasure(), measureMemberTypeStyleIndexMap, rowIndex, out);
				this.printlnRowEndTag(out);

				beforeKeys = keys;
				rowIndex++;
			}

			out.println("</Table>");

			// ウインドウ枠固定
			// データテーブルのセル(0行0列)の座標をエクセル内のインデックスに変換
			String horizontal = Integer.toString(5+report.getEdgeByType(Constants.Col).getAxisList().size());	// 
			String vertical = Integer.toString(0+report.getEdgeByType(Constants.Row).getAxisList().size());		// 
			out.println("<WorksheetOptions xmlns=\"urn:schemas-microsoft-com:office:excel\">");
				out.println("<FrozenNoSplit/>");
				out.println("<SplitHorizontal>" + horizontal + "</SplitHorizontal>");
				out.println("<TopRowBottomPane>" + horizontal + "</TopRowBottomPane>");
				out.println("<SplitVertical>" + vertical + "</SplitVertical>");
				out.println("<LeftColumnRightPane>"+ vertical + "</LeftColumnRightPane>");
				out.println("<ActivePane>0</ActivePane>");
			out.println("</WorksheetOptions>");


			out.println("</Worksheet>");
			out.println("</Workbook>");

			out.flush();
			osw.flush();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {

			if (fs != null) {
				try {
					fs.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}

			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}

		}

		// 一時保存したディメンションメンバー情報を削除する
		report.clearDimensionMembers();

		return "/spread/downloadAct.jsp";

	}


	// ********** privateメソッド **********

	/**
	 * 0段から指定された段までのキーの組み合わせがいくつ同じであるかを求める。
	 * @param spreadIndex SpreadのIndex
	 * @param hieIndex 段Index
	 * @param indexKeyList indexKeyのリスト
	 * @return 同じキーの組み合わせが続く数
	 */
	private int getFollowingSameKeyNums(int spreadIndex, int hieIndex, ArrayList indexKeyList) {

		String indexKey = (String) indexKeyList.get(spreadIndex);
		ArrayList indexKeyArray = StringUtil.splitString(indexKey, ":");
		String keys = (String) indexKeyArray.get(1);
		ArrayList keyList = StringUtil.splitString(keys, ";");

		int count = 0;
		for (int i = 0; i < (indexKeyList.size()-spreadIndex-1); i++ ){

			String nextIndexKey = (String) indexKeyList.get(spreadIndex + (i+1));
			ArrayList nextIndexKeys = StringUtil.splitString(nextIndexKey, ":");
			String nextkeys = (String) nextIndexKeys.get(1);
			ArrayList nextkeyList = StringUtil.splitString(nextkeys, ";");

			for (int j = 0; j < hieIndex+1; j++) {
				String key = (String) keyList.get(j);
				String nextKey = (String) nextkeyList.get(j);

				if (!key.equals(nextKey)) { // セル結合する領域から出た
					return count;
				}
			}
			count++;
		}
		return count;
	}


	/**
	 * セルに対して適用するスタイルのIDを求める
	 * @param colorInfo 色情報を格納するオブジェクト
	 * @param x 列ヘッダ、行ヘッダ、データテーブル内でのX座標
	 * @param y 列ヘッダ、行ヘッダ、データテーブル内でのY座標
	 * @return スタイルID
	 */
	private String getCellStyle(TableColorInfo colorInfo, int x, int y) {

		String target = colorInfo.getTarget();
		TreeMap targetMap = colorInfo.getTargetmap();
		Hashtable colorStyleTable = colorInfo.getColorStyletable();

		if (targetMap.containsKey(new Integer(y))) {
			TreeMap colMap = (TreeMap) targetMap.get(new Integer(y));
			if (colMap.containsKey(new Integer(x))) {
				String color = (String) colMap.get(new Integer(x));
				if (colorStyleTable.containsKey(color)){
					String styleID = (String) colorStyleTable.get(color);
					return styleID;
				} else {
					throw new IllegalStateException();
				}
			} else {
				return target;
			}
		} else { 
			return target;
		}
	}

	/**
	 * セルのスタイル（色・フォーマット）を出力
	 * @param colorTable 色とスタイルIDの対応表
	 * @param report レポートオブジェクト
	 * @param out PrintWriterオブジェクト
	 */
	private HashMap printColorAndFormatStyle(Hashtable colorTable, Report report, PrintWriter out) {

		HashMap measureMemberTypeStyleIndexMap = new HashMap();
		Measure measure = report.getMeasure();
		ArrayList targetMeasureMembers = null;
		if (report.isInPage(measure)) {
			targetMeasureMembers = new ArrayList();
			MeasureMember measureMember = null;
			String defaultMemberKey = measure.getDefaultMemberKey();
			if (defaultMemberKey == null) {
				measureMember = (MeasureMember) measure.getAxisMemberList().get(0);
			} else {
				measureMember = (MeasureMember)measure.getAxisMemberByUniqueName(defaultMemberKey);
			}
			targetMeasureMembers.add(measureMember);
		} else { // メジャーが行か列にある
			targetMeasureMembers = measure.getAxisMemberList();
		}


		// 色設定の無いセルのスタイルを出力
		int i = 0;
		Iterator meaMemIte = targetMeasureMembers.iterator();
		while (meaMemIte.hasNext()) {
			MeasureMember measureMember = (MeasureMember) meaMemIte.next();
			if (measureMemberTypeStyleIndexMap.containsKey(measureMember.getMeasureMemberType().getGroupName()) ) {
				continue;
			} else {
				measureMemberTypeStyleIndexMap.put(measureMember.getMeasureMemberType().getGroupName(), new Integer(i));
				this.outStyle(null, null, true, measureMember, Integer.toString(i), out);
				i++;
			}
		}

		// 色設定のされているセルのスタイルを出力
		Iterator colorIt = colorTable.keySet().iterator();
		if (colorTable.size()>0) {
			while (colorIt.hasNext()) {
				String color = (String) colorIt.next();
				String styleColorID = (String) colorTable.get(color);

				HashMap tmpMap = new HashMap(); // メジャーメンバータイプで既に使用されたグループは除外するために使用する
				Iterator ite = targetMeasureMembers.iterator();
				i = 0;
				while (ite.hasNext()) {

					MeasureMember measureMember = (MeasureMember) ite.next();
					if (tmpMap.containsKey(measureMember.getMeasureMemberType().getGroupName()) ) {
						continue;
					} else {
						tmpMap.put(measureMember.getMeasureMemberType().getGroupName(), new Integer(i));
						this.outStyle(styleColorID, color, true, measureMember, Integer.toString(i), out);
						i++;
					}
				}
			}
		}

		return measureMemberTypeStyleIndexMap;
	}


	/**
	 * スタイル（色・フォーマット）を出力
	 * @param styleColorID 色とスタイルIDの対応表
	 * @param color エクセルの色をあらわす文字列
	 * @param isDataCell データテーブルのセルであればtrue、それ以外であればfalse
	 * @param measureMember メジャーメンバーのオブジェクト
	 * @param dataID メジャーのメンバータイプから重複を排除し、Indexを振ったもの
	 * @param out PrintWriterオブジェクト
	 */
	private void outStyle(String styleColorID, String color, boolean isDataCell, MeasureMember measureMember, String dataID, PrintWriter out) {
		String styleID = null;
		if (isDataCell) { // データセルのスタイル
			if (styleColorID != null ) {
				styleID = styleColorID + "_" + dataID;

			} else {
				styleID = "sDATA" + "_" + dataID;
			}
		} else { // データセル以外(行ヘッダ、列ヘッダ)のスタイル
			styleID = styleColorID;
		}

		out.println("<Style ss:ID=\"" + styleID + "\">");
			out.println("<Borders>");
			out.println("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
			out.println("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
			out.println("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
			out.println("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"/>");
			out.println("</Borders>");
			if (color != null) {
				out.println("<Interior ss:Color=\"" + color + "\" ss:Pattern=\"Solid\"/>");
			}

			if ( isDataCell ) {
				out.println("<NumberFormat ss:Format=\"" + measureMember.getMeasureMemberType().getXMLSpreadsheetFormat() +  "\"/>");
			}
		out.println("</Style>");
	}



	/**
	 * 行ヘッダ、列ヘッダのスタイルを出力
	 * @param colorTable 色情報テーブル
	 * @param out PrintWriterオブジェクト
	 */
	private void printColorStyle(Hashtable colorTable, PrintWriter out) {

		Iterator colorIt = colorTable.keySet().iterator();

		while (colorIt.hasNext()) {
			String color = (String) colorIt.next();
			String styleColorID = (String) colorTable.get(color);

			this.outStyle(styleColorID,color,false, null,null,out);
		}
		
	}

	/**
	 * 色とスタイルIDの対応表を求める
	 * @param target 列ヘッダか、行ヘッダか、データテーブルかをあらわす
	 * @param rowColorMap 列ヘッダ、行ヘッダ、データテーブルの行をあらわすオブジェクト
	 * @return 色とスタイルIDの対応表
	 */
	private Hashtable getColorStyleList(String target, TreeMap rowColorMap) {
		
		Hashtable colorStyleIDTable = new Hashtable();
		
		Iterator rowIte = rowColorMap.keySet().iterator();
		int i=0;
		while (rowIte.hasNext()) {
			Integer y = (Integer) rowIte.next();
			TreeMap colColorMap = (TreeMap)rowColorMap.get(y);
			Iterator colIte = colColorMap.keySet().iterator();
			while (colIte.hasNext()) {
				Integer x = (Integer) colIte.next();
				String color = (String)colColorMap.get(x);

				if (!colorStyleIDTable.containsKey(color)) {
					colorStyleIDTable.put(color, (target + Integer.toString(i)));
					i++;
				}
			}
		}		
		
		return colorStyleIDTable;
	}

	/**
	 * クライアントからのリクエストより色情報を格納したTreeMapオブジェクトを求める
	 * @param helper RequestHelperオブジェクト
	 * @param target 列ヘッダか、行ヘッダか、データテーブルかをあらわす
	 * @return 色情報を格納したTreeMapオブジェクト
	 */
	private TreeMap getColorMap(RequestHelper helper, String target) {

		String indexColorInfo = "";
		if (Constants.Col.equals(target)) {
			indexColorInfo = helper.getRequest().getParameter("colHdrColor");
		} else if (Constants.Row.equals(target)) {
			indexColorInfo = helper.getRequest().getParameter("rowHdrColor");
		} else if (Constants.Data.equals(target)) {
			indexColorInfo = helper.getRequest().getParameter("dataHdrColor");
		}
		
		TreeMap rowTreeMap = new TreeMap();// 行ヘッダ、列ヘッダ、データ部の色情報を格納する

		ArrayList colorInfoList = StringUtil.splitString(indexColorInfo, ",");
		Iterator colorInfoIt = colorInfoList.iterator();
		while (colorInfoIt.hasNext()) {
			String indexColorString = (String) colorInfoIt.next();
			ArrayList IndexColor = StringUtil.splitString(indexColorString, ";");
			String indexes = (String)IndexColor.get(0);
			String webColor =(String)IndexColor.get(1); 

			// Webカラーを表すスタイル名をもとにエクセルの色を求める
			String xmlSpreadColor = null;
			if (Constants.Data.equals(target)) { // データテーブルの場合
				xmlSpreadColor = Messages.getString("ExportReportAsXMLSpreadsheetSchema.XMLSpreadsheetData" + webColor);
			} else { // 行・列ヘッダの場合
				xmlSpreadColor = Messages.getString("ExportReportAsXMLSpreadsheetSchema.XMLSpreadsheetHdr" + webColor);
			}
			ArrayList indexList = StringUtil.splitString(indexes, ":");

			Integer x = null;
			Integer y = null;
			Integer oldX = Integer.decode((String)indexList.get(0));
			Integer oldY = Integer.decode((String)indexList.get(1));

			// 表示セルのSpreadIndexを求める
			// ・Indexを表示対象となるメンバー(親がドリル未で非表示であるメンバー)のみで振りなおす
			// ・色づいているセルリストには表示対象とならないメンバー（親がドリル未で非表示であるメンバー）も含むが、その場合はスキップする
			if (Constants.Col.equals(target)) {
				if (xIndexChangeTable.containsKey(oldX)) {
					x = (Integer)xIndexChangeTable.get(oldX);
					y = oldY;// 列エッジにおける段Index
				} else {
					continue;
				}

			} else if (Constants.Row.equals(target)) {
				if (yIndexChangeTable.containsKey(oldY)) {
					x = oldX;// 行エッジにおける段Index
					y = (Integer)yIndexChangeTable.get(oldY);
				} else {
					continue;
				}
			} else { // データテーブルのセル
				if (!xIndexChangeTable.containsKey(oldX)) {
					continue;
				}
				if (!yIndexChangeTable.containsKey(oldY)) {
					continue;
				}
				x = (Integer)xIndexChangeTable.get(oldX);
				y = (Integer)yIndexChangeTable.get(oldY);	
			}

			TreeMap cellMap = null;
			if (rowTreeMap.containsKey(y)) {
				cellMap = (TreeMap) rowTreeMap.get(y);
				cellMap.put(x, xmlSpreadColor);
			} else {
				cellMap = new TreeMap();
				cellMap.put(x, xmlSpreadColor);
				rowTreeMap.put(y, cellMap);	
			}
			
		}

		return rowTreeMap;
	}


	/**
	 * クライアント側のSpreadIndexをKeyとし、今表示している行・列に対して新たに振りなおしたSpreadIndexをValueにもつIndex変換テーブルを返す。
	 * @param dataRowMap TreeMapオブジェクト
	 * @param targetCoordinates 座標
	 * @return Index変換テーブル
	 */
	private Hashtable getSpreadIndexChangeTable(TreeMap dataRowMap, String targetCoordinates) {
		if ((dataRowMap == null) || (targetCoordinates == null) ) { throw new IllegalArgumentException(); }
		if (targetCoordinates.equals("x") && (targetCoordinates.equals("y"))) { throw new IllegalArgumentException(); }

		Hashtable targetChangeTable = new Hashtable();
		
		Iterator oldRowIndexIt = dataRowMap.keySet().iterator();
		int i = 0;
		while (oldRowIndexIt.hasNext()) {
			Integer oldRowIndex = (Integer) oldRowIndexIt.next();

			if (targetCoordinates.equals("y")){
				targetChangeTable.put(oldRowIndex, new Integer(i));
			} else if (targetCoordinates.equals("x")) {
				TreeMap dataCellMap = (TreeMap) dataRowMap.get(oldRowIndex);
				Iterator oldColIndexIt = dataCellMap.keySet().iterator();
				int j = 0;
				while (oldColIndexIt.hasNext()) {
					Integer oldColIndex = (Integer) oldColIndexIt.next();
					targetChangeTable.put(oldColIndex, new Integer(j));
					j++;
				}
				break;
			}
			i++;
		}

//System.out.println("targetChangeTable" + targetCoordinates + (new HashMap(targetChangeTable)));

		return targetChangeTable;
	
	}


	/**
	 * データセル部を一行出力する
	 * @param dataRowMap
	 * @param dataTableColorInfo
	 * @param measure
	 * @param measureMemberTypeStyleIndexMap
	 * @param newRowIndex
	 * @param out PrintWriterオブジェクト
	 */
	private void printDataRow(TreeMap dataRowMap, TableColorInfo dataTableColorInfo, Measure measure, HashMap measureMemberTypeStyleIndexMap, int newRowIndex, PrintWriter out) {
//System.out.println("measureMemberTypeStyleIndexMap:" + measureMemberTypeStyleIndexMap);

		// 取得対象となる行のクライアント側でのSpreadIndexの値と、取得対象となる行に対して、通番で振られるIndexを変換する
		ArrayList oldRowIndex = new ArrayList(dataRowMap.keySet());
		TreeMap dataCellMap = (TreeMap) dataRowMap.get(oldRowIndex.get(newRowIndex));
		Iterator dataCellIt = dataCellMap.keySet().iterator();
		int x = 0;
		while (dataCellIt.hasNext()) {
			Integer colIndex = (Integer) dataCellIt.next();
			CellData cellData = (CellData)dataCellMap.get(colIndex);
			String groupName = ((MeasureMember)measure.getAxisMemberByUniqueName(cellData.getMeasureMemberUniqueName())).getMeasureMemberType().getGroupName();
			String value = cellData.getValue();
			String styleID = getCellStyle(dataTableColorInfo, x, newRowIndex) + "_" + measureMemberTypeStyleIndexMap.get(groupName);
			this.printlnCell(value, styleID, 0, 0, 0, "Number", out);
			x++;
		}
	}


	/**
	 * 行開始タグを出力する
	 */
	private void printlnRowStartTag(PrintWriter out) {
		out.println("<Row>");
	}

	/**
	 * 行終了タグを出力する
	 */
	private void printlnRowEndTag(PrintWriter out) {
		out.println("</Row>");
	}

	/**
	 * セルを出力する
	 * @param value 値
	 * @param cellStyle スタイルID
	 * @param cellIndex セルの列インデックス
	 * @param mergeAcross セルを右方向に結合する数
	 * @param mergeDown セルを下方向に結合する数
	 * @param dataType セルのデータタイプ（String,Number等）
	 * @param out PrintWriterオブジェクト
	 */
	private void printlnCell(String value, String cellStyle, int cellIndex, int mergeAcross, int mergeDown, String dataType, PrintWriter out) {

		String styleString = "";
		String dataString  = "";
			if ( cellStyle != "" ) {
				styleString = " ss:StyleID=\"" + cellStyle + "\"";
			}
			if ( dataType != "" ) {
				dataString = " ss:Type=\"" + dataType + "\"";
			} else {
				dataString = " ss:Type=\"String\"";
			}

		String cellMergeIndexString = "";
			if (cellIndex>0){
				cellMergeIndexString = " ss:Index=\"" + Integer.toString(cellIndex) + "\"";
			}

		String mergeAcrossString = "";
			if (mergeAcross > 0) {
				mergeAcrossString = " ss:MergeAcross=\"" + Integer.toString(mergeAcross) + "\"";
			}

		String mergeDownString = "";
			if (mergeDown > 0) {
				mergeDownString = " ss:MergeDown=\"" + Integer.toString(mergeDown) + "\"";
			}

		out.println("<Cell" + cellMergeIndexString + mergeAcrossString + mergeDownString + styleString + ">");
		out.print("<Data" + dataString + ">");
		out.print(value);
		out.println("</Data>");
		out.println("</Cell>");
		
	}

	// ********** innerクラス **********

	/**
	 *  Innerクラス：TableColorInfo
	 *  説明：色情報を格納するクラスです。
	 */
	class TableColorInfo {

		String target   = "";
		TreeMap   targetMap = null;
		Hashtable colorStyleTable = null;
	
		public TableColorInfo(String target, TreeMap targetMap, Hashtable colorStyleTable) {
			this.target = target;
			this.targetMap = targetMap;
			this.colorStyleTable = colorStyleTable;			
		}
	
		Hashtable getColorStyletable() {
			return colorStyleTable;
		}
		String getTarget() {
			return target;
		}
		TreeMap getTargetmap() {
			return targetMap;
		}

		void setColorStyletable(Hashtable hashtable) {
			colorStyleTable = hashtable;
		}
		void setTarget(String string) {
			target = string;
		}
		void setTargetmap(TreeMap map) {
			targetMap = map;
		}

	}

}
