package hiro.yoshioka.sdh;

import hiro.yoshioka.util.LineSeparatorEnum;
import hiro.yoshioka.util.SQLUtil;
import hiro.yoshioka.util.StringUtil;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class StringRecordDataHolder implements Serializable {
	private static final String HTML_HEAD;
	private static final String HTML_HEAD_WITH_PAGER;
	static {
		StringBuilder pagerBuf = new StringBuilder();
		pagerBuf.append(String.format("	 %n"));
		pagerBuf.append(String.format("	var page = 0; %n"));
		pagerBuf.append(String.format("	function draw() { %n"));
		pagerBuf.append(String.format("		$('#page').html('P.'+(page + 1)); %n"));
		pagerBuf.append(String.format("		$('tr').hide(); %n"));
		pagerBuf.append(String
				.format("		$('tr:first,tr:gt(' + page * 10 + '):lt(10)').show(); %n"));
		pagerBuf.append(String.format("	} %n"));
		pagerBuf.append(String.format("	$('#showAll').click(function() { %n"));
		pagerBuf.append(String.format("		$('tr').show(); %n"));
		pagerBuf.append(String.format("		$('#prev').hide(); %n"));
		pagerBuf.append(String.format("		$('#next').hide(); %n"));
		pagerBuf.append(String.format("		$('#page').hide(); %n"));
		pagerBuf.append(String.format("		$('#showAll').hide(); %n"));
		pagerBuf.append(String.format("	}); %n"));
		pagerBuf.append(String.format("	$('#prev').click(function() { %n"));
		pagerBuf.append(String.format("		if (page > 0) { %n"));
		pagerBuf.append(String.format("			page--; %n"));
		pagerBuf.append(String.format("			draw(); %n"));
		pagerBuf.append(String.format("		} %n"));
		pagerBuf.append(String.format("	}); %n"));
		pagerBuf.append(String.format("	$('#next').click(function() { %n"));
		pagerBuf.append(String
				.format("		if (page < ($('tr').size() - 1) / 10 - 1) { %n"));
		pagerBuf.append(String.format("			page++; %n"));
		pagerBuf.append(String.format("			draw(); %n"));
		pagerBuf.append(String.format("		} %n"));
		pagerBuf.append(String.format("	}); %n"));
		pagerBuf.append(String.format("	draw(); %n"));

		StringBuilder buf = new StringBuilder();

		buf.append(String.format("<head> %n"));
		buf.append(String
				.format("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /> %n"));
		buf.append(String
				.format("<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" /> %n"));
		// <title>sample1</title> %n"));
		buf.append(String
				.format("<script type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js\"></script> %n"));
		buf.append(String.format("<script type=\"text/javascript\"> %n"));
		buf.append(String.format("$(function(){ %n"));
		buf.append(String
				.format("	$(\"th:nth-child(odd)\").addClass(\"odd\"); %n"));
		buf.append(String
				.format("	$(\"tr:nth-child(even)\").addClass(\"even\"); %n"));
		buf.append(String.format("	$(\"tr\").mouseover(function(){ %n"));
		buf.append(String.format("		$(this).addClass(\"hover\"); %n"));
		buf.append(String.format("	}).mouseout(function(){ %n"));
		buf.append(String.format("		$(this).removeClass(\"hover\"); %n"));
		buf.append(String.format("	}); %n"));

		buf.append(String.format("	$('td.bars').each(function(i){ %n"));
		buf.append(String.format("		var tdText = $(this).text(); %n"));
		buf.append(String.format("		var tdNum = parseInt(tdText, 10); %n"));
		buf.append(String.format("		if ( ! isNaN(tdNum) ) { %n"));
		buf.append(String.format("			if( tdNum > 100 ) { %n"));
		buf.append(String.format("				tdNum = 100; %n"));
		buf.append(String.format("			} %n"));
		buf.append(String.format("			$(this).css('padding','1px'); %n"));
		buf.append(String.format("			if ( tdNum == 0 ) { %n"));
		buf.append(String.format("				$(this).text('0%%'); %n"));
		buf.append(String.format("			} else if ( tdNum < 15 ) { %n"));
		buf.append(String
				.format("				$(this).html(\"<div style='margin:0px; float:left;text-align:center;width:\"+tdNum+\"%%;border: 2px solid green;background-color:lime;'>&nbsp;</div>&nbsp;&nbsp;\"+tdNum+\"%%\"); %n"));
		buf.append(String.format("			} else if ( tdNum < 33 ) { %n"));
		buf.append(String
				.format("				$(this).html(\"<div style='margin:0px; text-align:center;width:\"+tdNum+\"%%;border: 2px solid navy;background-color:blue;color:white;'>\" + tdNum + \"%%</div>\"); %n"));
		buf.append(String.format("			} else if ( tdNum < 66 ) { %n"));
		buf.append(String
				.format("				$(this).html(\"<div style='margin:0px; text-align:center;width:\"+tdNum+\"%%;border: 2px solid purple;background-color:fuchsia;color:white;'>\" + tdNum + \"%%</div>\"); %n"));
		buf.append(String.format("			} else { %n"));
		buf.append(String
				.format("				$(this).html(\"<div style='margin:0px; text-align:center;width:\"+tdNum+\"%%;border: 2px solid maroon;background-color:red;color:white;'>\" + tdNum + \"%%</div>\"); %n"));
		buf.append(String.format("			} %n"));
		buf.append(String.format("		} %n"));
		buf.append(String.format("	}); %n"));

		String pre = buf.toString();
		buf.setLength(0);

		buf.append(String.format("}); %n"));
		buf.append(String.format("</script> %n"));
		buf.append(String.format("<style type=\"text/css\"> %n"));
		// buf.append(String.format("	table{ %n"));
		// buf.append(String.format("		margin:100px auto; %n"));
		// buf.append(String.format("	} %n"));
		buf.append(String.format("	th.odd{ %n"));
		buf.append(String.format("		background:#444444; %n"));
		buf.append(String.format("	} %n"));
		buf.append(String.format("	th{ %n"));
		buf.append(String.format("		background:#222222; %n"));
		buf.append(String.format("		color:white; %n"));
		buf.append(String.format("	} %n"));
		buf.append(String.format("	th,td{ %n"));
		buf.append(String.format("		padding:5px; %n"));
		buf.append(String.format("		font-size:small; %n"));
		buf.append(String.format("	} %n"));
		buf.append(String.format("	.even{ %n"));
		buf.append(String.format("		background:#F2F2F2; %n"));
		buf.append(String.format("	} %n"));
		buf.append(String.format("	.hover{ %n"));
		buf.append(String.format("		background:#B2D8FF; %n"));
		buf.append(String.format("	} %n"));
		buf.append(String.format("	#showAll, #prev, #next { %n"));
		buf.append(String.format("		color: red; %n"));
		buf.append(String.format("		cursor: pointer; %n"));
		buf.append(String.format("	} %n"));
		buf.append(String.format("</style> %n"));
		buf.append(String.format("</head> %n"));

		HTML_HEAD = pre + buf.toString();
		HTML_HEAD_WITH_PAGER = pre + pagerBuf + buf.toString();
	}

	private static final long serialVersionUID = 2584643902488450916L;
	protected transient Log log = LogFactory.getLog(this.getClass());
	protected String[] key;
	protected List<StringRecordData[]> list = new ArrayList<StringRecordData[]>();

	/**
	 * @param argNames
	 * @throws NullPointerException
	 */
	public StringRecordDataHolder(String[] argNames)
			throws NullPointerException {
		key = argNames;
	}

	public StringRecordDataHolder() {
	}

	public int getRowCount() {
		return list.size();
	}

	public int getIndexByName(String name) {
		for (int i = 0; i < key.length; i++) {
			if (key[i].equalsIgnoreCase(name)) {
				return i;
			}
		}
		return -1;
	}

	public int getRightIndexByName(String name) {
		int i = getIndexByName(name);

		if (key.length > (i + 1)) {
			return i + 1;
		}
		return -1;
	}

	protected StringRecordData[] createEmptyRow() {
		StringRecordData[] ret = new StringRecordData[key.length];
		for (int i = 0; i < ret.length; i++) {
			ret[i] = new StringRecordData(StringUtil.EMPTY_STRING);
		}
		return ret;
	}

	public synchronized void deleteColumn(int insertIndex) {
		String[] key2 = Arrays.copyOf(key, key.length - 1);

		for (int i = insertIndex; i < key2.length; i++) {
			key2[i] = key[i + 1];
		}
		key = key2;

		int loopSize = list.size();
		for (int i = 0; i < loopSize; i++) {
			StringRecordData[] line = list.get(i);
			StringRecordData[] line2 = Arrays.copyOf(line, line.length - 1);

			for (int j = insertIndex; j < line2.length; j++) {
				line2[j] = line[j + 1];
			}
			list.remove(i);
			list.add(i, line2);
		}
	}

	public synchronized void addColumn(String headerTitle, String[] columnDatum) {
		insertColumn(getKey().length, headerTitle, columnDatum);
	}

	public synchronized void insertColumn(int insertIndex, String headerTitle,
			String[] columnDatum) {
		key = Arrays.copyOf(key, key.length + 1);

		for (int i = key.length - 1; i > insertIndex; i--) {
			key[i] = key[i - 1];
		}
		key[insertIndex] = headerTitle;

		int loopSize = list.size();
		for (int i = 0; i < loopSize; i++) {
			StringRecordData[] line = list.get(i);
			line = Arrays.copyOf(line, line.length + 1);

			for (int j = line.length - 1; j > insertIndex; j--) {
				line[j] = line[j - 1];
			}
			if (columnDatum != null && columnDatum.length > i) {
				line[insertIndex] = new StringRecordData(columnDatum[i]);
			} else {
				line[insertIndex] = new StringRecordData(
						StringUtil.EMPTY_STRING);
			}
			list.remove(i);
			list.add(i, line);
		}
	}

	public void addRow(StringRecordData[] datas) {
		list.add(datas);
	}

	protected StringRecordData[] createNewIndexOf(String[] datas, int f, int l) {
		String[] newdata = new String[l - f + 1];

		for (int i = 0, j = f; i < newdata.length; i++, j++) {
			newdata[i] = datas[j];
		}
		return StringRecordData.createStringRecordData(newdata);
	}

	public void addEmptyRow() {
		list.add(createEmptyRow());
	}

	public boolean hasColumn(String columnName) {
		return getIndexByName(columnName) >= 0;
	}

	public void addEmptyRow(int idx) {
		list.add(idx, createEmptyRow());
	}

	public void addRow(String[] datas) {
		addRow(StringRecordData.createStringRecordData(datas));
	}

	public List<String[]> getAllRecordList() {
		return getAllRecordList(false);
	}

	public List<String[]> getAllRecordList(boolean withHeader) {
		return getAllRecordList(withHeader, true);
	}

	public List<String[]> getAllRecordList(boolean withHeader,
			boolean withRowColumn) {
		List<String[]> retList = new ArrayList<String[]>();
		for (int i = 0; i < list.size(); i++) {
			if (withRowColumn) {
				retList.add(getRow(i));
			} else {
				retList.add(getRowWithoutRowColumn(i));
			}
		}
		if (withHeader) {
			if (withRowColumn) {
				retList.add(0, key);
			} else {
				retList.add(0, Arrays.copyOfRange(key, 1, key.length));
			}
		}
		return retList;
	}

	public String[] getRow(int row) {
		StringRecordData[] data = (StringRecordData[]) list.get(row);
		String[] ret = new String[data.length];
		for (int i = 0; i < data.length; i++) {
			ret[i] = data[i].getString();
		}
		return ret;
	}

	public String[] getRowWithoutRowColumn(int row) {
		StringRecordData[] data = (StringRecordData[]) list.get(row);
		String[] ret = new String[data.length - 1];
		for (int i = 1; i < data.length; i++) {
			ret[i - 1] = data[i].getString();
		}
		return ret;
	}

	public Set<StringRecordData> getPartOfStringRecordRow(int row,
			Set<Integer> colIdxSet) {
		if (colIdxSet == null || colIdxSet.size() == 0) {
			return Collections.EMPTY_SET;
		}
		Set<StringRecordData> retSet = new LinkedHashSet<StringRecordData>();
		StringRecordData[] rowData = this.list.get(row);
		for (Integer i : colIdxSet) {
			retSet.add(rowData[i]);
		}
		return retSet;
	}

	public StringRecordData[] getStringRecordRow(int row) {
		return (StringRecordData[]) list.get(row);
	}

	public String getStringData(int row, String key) {
		String[] rowData = getRow(row);
		return rowData[getIndexByName(key)];
	}

	public void replaceStringData(int row, String key, String value) {
		StringRecordData[] rowData = getStringRecordRow(row);
		rowData[getIndexByName(key)].setString(value);
	}

	public Date getDateData(int row, String key) {
		return getDateData(row, getIndexByName(key));
	}

	public Date getDateData(int row, int index) {
		String[] rowData = getRow(row);
		try {
			return SQLUtil
					.getDateOfUtil(SQLUtil.getDate(rowData[index], false));
		} catch (Exception e) {
			if (log.isTraceEnabled()) {
				log.trace("Exception ROW[" + row + "] [" + rowData[index] + "]");
			}
		}
		return null;
	}

	public Integer getIntData(int row, int index) {
		String[] rowData = getRow(row);
		try {
			return new Integer(rowData[index]);
		} catch (NumberFormatException e) {
			if (log.isTraceEnabled()) {
				log.trace("NumberFormatException ROW[" + row + "] ["
						+ rowData[index] + "]");
			}
		}
		return null;
	}

	public Boolean getBooleanData(int row, String key) {
		return this.getBooleanData(row, getIndexByName(key));
	}

	public Boolean getBooleanData(int row, int index) {
		String[] rowData = getRow(row);
		return SQLUtil.getBoolean(rowData[index]);
	}

	public int getIntDataDefaultZero(int row, String key) {
		Integer ret = getIntData(row, getIndexByName(key));
		if (ret == null) {
			return 0;
		}
		return ret.intValue();
	}

	public Integer getIntData(int row, String key) {
		return getIntData(row, getIndexByName(key));
	}

	public String[][] getStringData() {
		int cnt = getRowCount();
		String[][] ret = new String[cnt][key.length];
		for (int i = 0; i < cnt; i++) {
			ret[i] = getRow(i);
		}
		return ret;
	}

	public StringRecordData[][] getStringRecordData() {
		int cnt = getRowCount();
		StringRecordData[][] ret = new StringRecordData[cnt][key.length];
		for (int i = 0; i < cnt; i++) {
			ret[i] = getStringRecordRow(i);
		}
		return ret;
	}

	public StringRecordPairData getPair(int row, int col) {
		return getStringRecordRow(row)[col].getPair();
	}

	public void setPair(int row, int col, StringRecordPairData pair) {
		StringRecordData data = ((StringRecordData[]) list.get(row))[col];
		data.setPair(pair);
	}

	public void setPairAll(StringRecordPairData pair) {
		for (int row = 0; row < list.size(); row++) {
			for (int col = 0; col < key.length; col++) {
				setPair(row, col, pair.createCopy());
			}
		}
	}

	public int indexOf(Properties searchProperties) {
		int[] searchIndices = new int[searchProperties.size()];
		int cnt = 0;
		for (Object sk : searchProperties.keySet()) {
			searchIndices[cnt] = getIndexByName((String) sk);
			if (searchIndices[cnt] < 0) {
				return -1;
			}
			cnt++;
		}
		nextSearch: for (int row = 0; row < list.size(); row++) {
			for (int searchIdx : searchIndices) {
				StringRecordData srd = list.get(row)[searchIdx];
				if (!srd.getString().equals(
						searchProperties.getProperty(getKey()[searchIdx]))) {
					continue nextSearch;
				}
			}
			return row;
		}
		return -1;
	}

	public int indexOf(String key, String searchText) {
		int idx = getIndexByName(key);
		if (idx < 0) {
			return idx;
		}
		for (int row = 0; row < list.size(); row++) {
			StringRecordData srd = list.get(row)[idx];
			if (srd.getString().equals(searchText)) {
				return row;
			}
		}
		return -1;
	}

	public synchronized void sort(final Properties sortProperties) {
		int cnt = 0;
		final int[] searchIndices = new int[sortProperties.size()];
		final boolean[] compareInt = new boolean[sortProperties.size()];
		for (Object sk : sortProperties.keySet()) {
			searchIndices[cnt] = getIndexByName((String) sk);
			if (searchIndices[cnt] < 0) {
				return;
			}
			compareInt[cnt] = Integer.class.equals(sortProperties.get(sk));
			cnt++;
		}
		Collections.sort(list, new Comparator<StringRecordData[]>() {
			@Override
			public int compare(StringRecordData[] o1, StringRecordData[] o2) {
				for (int i = 0; i < searchIndices.length; i++) {
					int idx = searchIndices[i];
					if (compareInt[i]) {
						int i1 = o1[idx].getInt();
						int i2 = o2[idx].getInt();
						if (i1 - i2 == 0) {
							continue;
						}
						return i1 - i2;
					} else {
						int result = o1[idx].getString().compareTo(
								o2[idx].getString());
						if (result == 0) {
							continue;
						}
						return result;
					}
				}
				return 0;
			}
		});
	}

	public void sort(final String key, final boolean compareInt) {
		final int idx = getIndexByName(key);
		if (idx < 0) {
			return;
		}
		Collections.sort(list, new Comparator<StringRecordData[]>() {
			@Override
			public int compare(StringRecordData[] o1, StringRecordData[] o2) {
				if (compareInt) {
					int i1 = o1[idx].getInt();
					int i2 = o2[idx].getInt();
					return i1 - i2;
				} else {
					return o1[idx].getString().compareTo(o2[idx].getString());
				}
			}
		});
	}

	public String[] getKey() {
		return key;
	}

	public void dumpPair() {
		for (int i = 0; i < getRowCount(); i++) {
			StringRecordData[] datas = getStringRecordRow(i);
			for (int j = 0; j < datas.length; j++) {
				System.out.print(datas[j].getPair());
			}
			System.out.println();
		}
	}

	/**
	 * @param index
	 * @param i
	 * @param text
	 */
	public void changeString(int index, int i, String text) {
		StringRecordData[] data = (StringRecordData[]) list.get(index);
		data[i].setString(text);
	}

	/**
	 * @param index
	 * @param keyName
	 * @param text
	 */
	public void changeString(int index, String keyName, String text) {
		StringRecordData[] data = (StringRecordData[]) list.get(index);
		data[getIndexByName(keyName)].setString(text);
	}

	/**
	 * @param matchingKeyName
	 * @param matchingValue
	 * @param changeKeyName
	 * @param changeValue
	 */
	public void changeStringMatchedRow(String matchingKeyName,
			String matchingValue, String changeKeyName, String changeValue) {
		for (StringRecordData[] data : list) {
			if (data[getIndexByName(matchingKeyName)].getString().equals(
					matchingValue)) {
				data[getIndexByName(changeKeyName)].setString(changeValue);
			}
		}
	}

	/**
	 * height:200px, width:420px
	 * 
	 * @param maxRow
	 * @return
	 */
	public String toHtmlString(int maxRow) {
		return toHtmlString(maxRow, Collections.EMPTY_LIST);
	}

	public String toHtmlString(int maxRow, List<Integer> percentColIdxList) {
		StringBuilder buf = new StringBuilder();
		if (getRowCount() > 10) {
			buf.append(HTML_HEAD_WITH_PAGER);
			buf.append(StringUtil.LINE_SEPARATOR);
			buf.append("<span id='showAll'> [Show_All] </span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
			buf.append("<span id='prev'> [Prev] </span>&nbsp;&nbsp;");
			buf.append("<span id='page'></span>&nbsp;&nbsp;");
			buf.append("<span id='next'> [Next] </span>");
			buf.append(StringUtil.LINE_SEPARATOR);
		} else {
			buf.append(HTML_HEAD);
			buf.append(StringUtil.LINE_SEPARATOR);
		}

		buf.append("<table border=1><thead>");
		buf.append(StringUtil.LINE_SEPARATOR);
		buf.append("<tr>");
		for (int i = 0; i < getKey().length; i++) {
			buf.append("<th>").append(StringUtil.esc(getKey()[i]))
					.append("</th>");
		}
		buf.append("</tr>");
		buf.append(StringUtil.LINE_SEPARATOR);
		buf.append("</thead>");
		buf.append(StringUtil.LINE_SEPARATOR);

		if (getRowCount() > 0) {
			buf.append("<tbody>");
			for (int j = 0; j < getRowCount(); j++) {
				if (j % 2 == 1) {
					buf.append("<tr class=\"odd\">");
				} else {
					buf.append("<tr>");
				}
				toHtmlStringTBodyLine(j, percentColIdxList, buf);
				if (j >= maxRow) {
					buf.append("</tr>");
					buf.append(StringUtil.LINE_SEPARATOR);
					buf.append("<tr>");
					String[] data = getRow(j);
					buf.append("<td colspan=\"").append(data.length)
							.append("\">   --- and more ---   </td>");
					buf.append("</tr>");
					buf.append(StringUtil.LINE_SEPARATOR);
					break;
				}
				buf.append("</tr>");
				buf.append(StringUtil.LINE_SEPARATOR);
			}
			buf.append("</tbody>");
		}
		buf.append("</table>");
		return buf.toString();
	}

	protected void toHtmlStringTBodyLine(int rowIdx,
			List<Integer> percentColIdxList, StringBuilder buf) {
		String[] data = getRow(rowIdx);
		for (int i = 0; i < data.length; i++) {
			String str = StringUtil.esc(data[i]);
			str = StringUtil.resetCRLF(str, false, LineSeparatorEnum.HTML_BR);
			if (percentColIdxList.contains((i % data.length))) {
				buf.append("<td class='bars'>").append(str).append("</td>");
			} else {
				buf.append("<td>").append(str).append("</td>");
			}
		}
	}

	public void any(String key, Set<String> values) {
		int idx = getIndexByName(key);
		List<String> foundList = new ArrayList<String>();
		for (int iRow = 0; iRow < getRowCount(); iRow++) {
			String v = ((StringRecordData[]) list.get(iRow))[idx].getString();
			if (values.contains(v)) {
				foundList.add(v);
				continue;
			}
		}
		System.out.println("=== any ===");
		System.out.println("found[" + foundList.size() + "]");
		System.out.println(foundList);
		System.out.println("=== not found ===");
		values.removeAll(foundList);
		System.out.println("not found[" + values.size() + "]");
		System.out.println(values);
	}

	public void anyPart(String key, Set<String> values) {
		int idx = getIndexByName(key);
		List<String> foundList = new ArrayList<String>();
		for (int iRow = 0; iRow < getRowCount(); iRow++) {
			String v = ((StringRecordData[]) list.get(iRow))[idx].getString();
			for (String a : values) {
				if (v.indexOf(a) >= 0) {
					foundList.add(a);
					continue;
				}
			}
		}
		System.out.println("=== any ===");
		System.out.println("found[" + foundList.size() + "]");
		System.out.println(foundList);
		System.out.println("=== not found ===");
		values.removeAll(foundList);
		System.out.println("not found[" + values.size() + "]");
		System.out.println(values);
	}

	public void dumpRow(int row) {
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < getKey().length; i++) {
			buf.append(getKey()[i]).append("\t");
		}
		if (buf.length() > 0) {
			buf.setLength(buf.length() - 1);
		}
		buf.append(StringUtil.LINE_SEPARATOR);
		String[] data = getRow(row);
		for (int i = 0; i < data.length; i++) {
			buf.append(data[i]).append("\t");
		}
		if (buf.length() > 0) {
			buf.setLength(buf.length() - 1);
		}
		buf.append(StringUtil.LINE_SEPARATOR);
		buf.append("row count=").append(getRowCount());
		System.out.println(buf);
	}

	public String toString(int maxRow) {
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < getKey().length; i++) {
			buf.append(getKey()[i]).append("\t");
		}
		if (buf.length() > 0) {
			buf.setLength(buf.length() - 1);
		}
		if (getRowCount() > 0) {
			for (int j = 0; j < getRowCount(); j++) {
				buf.append(StringUtil.LINE_SEPARATOR);
				String[] data = getRow(j);
				for (int i = 0; i < data.length; i++) {
					buf.append(data[i]).append("\t");
				}
				if (buf.length() > 0) {
					buf.setLength(buf.length() - 1);
				}
				if (j >= maxRow) {
					buf.append(StringUtil.LINE_SEPARATOR);
					buf.append("   --- and more ---   ");
					break;
				}
			}
		}
		buf.append(StringUtil.LINE_SEPARATOR);
		buf.append("row count=").append(getRowCount());
		return buf.toString();
	}

	public String toString() {
		return toString(Integer.MAX_VALUE);
	}

	/**
	 * @param same
	 */
	public void moveTail(int index) {
		list.add(list.remove(index));
	}

	private void readObject(ObjectInputStream in) throws IOException,
			ClassNotFoundException {
		in.defaultReadObject();
		log = LogFactory.getLog(this.getClass());
	}

	public void deleteRow(int row) {
		list.remove(row);
	}

	public void deleteRowsGreatorThan(int deleteStartIndex) {
		while (list.size() > deleteStartIndex) {
			list.remove(deleteStartIndex);
		}
	}

	public void slice(int offset) {
		slice(offset, list.size() - offset);
	}

	public void slice(int offset, int num) {
		int s = offset + num;
		while (list.size() > (s)) {
			list.remove(s);
		}
		int loop = offset;
		for (int i = 0; i < loop; i++) {
			list.remove(0);
		}
	}

}