#ODFTOOLKIT-388# fixed by Raimund Hoelle - Test hangs when iterating over a spreadsheet created with LibreOffice 4.0.0

git-svn-id: https://svn.apache.org/repos/asf/incubator/odf/trunk@1715398 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/simple/src/main/java/org/odftoolkit/simple/SpreadsheetDocument.java b/simple/src/main/java/org/odftoolkit/simple/SpreadsheetDocument.java
index 12b96b9..550c3a2 100644
--- a/simple/src/main/java/org/odftoolkit/simple/SpreadsheetDocument.java
+++ b/simple/src/main/java/org/odftoolkit/simple/SpreadsheetDocument.java
@@ -1,666 +1,639 @@
-/************************************************************************
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
- * 
- * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
- * 
- * Use is subject to license terms.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also
- * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * 
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ************************************************************************/
-package org.odftoolkit.simple;
-
-import java.awt.Rectangle;
-import java.io.File;
-import java.io.InputStream;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.xpath.XPathConstants;
-
-import org.odftoolkit.odfdom.dom.OdfContentDom;
-import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;
-import org.odftoolkit.odfdom.dom.element.office.OfficeSpreadsheetElement;
-import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
-import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
-import org.odftoolkit.odfdom.pkg.MediaType;
-import org.odftoolkit.odfdom.pkg.OdfElement;
-import org.odftoolkit.odfdom.pkg.OdfPackage;
-import org.odftoolkit.odfdom.type.CellRangeAddressList;
-import org.odftoolkit.simple.chart.AbstractChartContainer;
-import org.odftoolkit.simple.chart.Chart;
-import org.odftoolkit.simple.chart.ChartContainer;
-import org.odftoolkit.simple.chart.DataSet;
-import org.odftoolkit.simple.table.Cell;
-import org.odftoolkit.simple.table.Table;
-import org.odftoolkit.simple.table.TableContainer;
-import org.w3c.dom.Node;
-
-/**
- * This class represents an empty ODF spreadsheet document.
- * 
- */
-public class SpreadsheetDocument extends Document implements ChartContainer {
-
-	private static final String EMPTY_SPREADSHEET_DOCUMENT_PATH = "/OdfSpreadsheetDocument.ods";
-	static final Resource EMPTY_SPREADSHEET_DOCUMENT_RESOURCE = new Resource(EMPTY_SPREADSHEET_DOCUMENT_PATH);
-	private ChartContainerImpl chartContainerImpl;
-	/**
-	 * This enum contains all possible media types of SpreadsheetDocument
-	 * documents.
-	 */
-	public enum OdfMediaType implements MediaType {
-
-		SPREADSHEET(Document.OdfMediaType.SPREADSHEET), SPREADSHEET_TEMPLATE(Document.OdfMediaType.SPREADSHEET_TEMPLATE);
-		private final Document.OdfMediaType mMediaType;
-
-		OdfMediaType(Document.OdfMediaType mediaType) {
-			this.mMediaType = mediaType;
-		}
-
-		/**
-		 * @return the mediatype of this document
-		 */
-		public String getMediaTypeString() {
-			return mMediaType.getMediaTypeString();
-		}
-
-		/**
-		 * @return the ODF filesuffix of this document
-		 */
-		public String getSuffix() {
-			return mMediaType.getSuffix();
-		}
-
-		/**
-		 * 
-		 * @param mediaType
-		 *            string defining an ODF document
-		 * @return the according OdfMediatype encapuslating the given string and
-		 *         the suffix
-		 */
-		public static Document.OdfMediaType getOdfMediaType(String mediaType) {
-			return Document.OdfMediaType.getOdfMediaType(mediaType);
-		}
-	}
-
-	/**
-	 * Creates an empty spreadsheet document.
-	 * 
-	 * @return ODF spreadsheet document based on a default template*
-	 * @throws java.lang.Exception
-	 *             - if the document could not be created
-	 */
-	public static SpreadsheetDocument newSpreadsheetDocument() throws Exception {
-		return (SpreadsheetDocument) Document.loadTemplate(EMPTY_SPREADSHEET_DOCUMENT_RESOURCE,
-				Document.OdfMediaType.SPREADSHEET);
-	}
-
-	/**
-	 * Creates an empty spreadsheet template.
-	 * 
-	 * @return ODF spreadsheet template based on a default
-	 * @throws java.lang.Exception
-	 *             - if the template could not be created
-	 */
-	public static SpreadsheetDocument newSpreadsheetTemplateDocument() throws Exception {
-		SpreadsheetDocument doc = (SpreadsheetDocument) Document.loadTemplate(EMPTY_SPREADSHEET_DOCUMENT_RESOURCE,
-				Document.OdfMediaType.SPREADSHEET_TEMPLATE);
-		doc.changeMode(OdfMediaType.SPREADSHEET_TEMPLATE);
-		return doc;
-	}
-
-	/**
-	 * To avoid data duplication a new document is only created, if not already
-	 * opened. A document is cached by this constructor using the internalpath
-	 * as key.
-	 */
-	protected SpreadsheetDocument(OdfPackage pkg, String internalPath, SpreadsheetDocument.OdfMediaType odfMediaType) {
-		super(pkg, internalPath, odfMediaType.mMediaType);
-	}
-
-	/**
-	 * Creates an SpreadsheetDocument from the OpenDocument provided by a
-	 * resource Stream.
-	 * 
-	 * <p>
-	 * Since an InputStream does not provide the arbitrary (non sequentiell)
-	 * read access needed by SpreadsheetDocument, the InputStream is cached.
-	 * This usually takes more time compared to the other createInternalDocument
-	 * methods. An advantage of caching is that there are no problems
-	 * overwriting an input file.
-	 * </p>
-	 * 
-	 * <p>
-	 * If the resource stream is not a ODF spreadsheet document,
-	 * ClassCastException might be thrown.
-	 * </p>
-	 * 
-	 * @param inputStream
-	 *            - the InputStream of the ODF spreadsheet document.
-	 * @return the spreadsheet document created from the given InputStream
-	 * @throws java.lang.Exception
-	 *             - if the document could not be created.
-	 */
-	public static SpreadsheetDocument loadDocument(InputStream inputStream) throws Exception {
-		return (SpreadsheetDocument) Document.loadDocument(inputStream);
-	}
-
-	/**
-	 * Loads an SpreadsheetDocument from the provided path.
-	 * 
-	 * <p>
-	 * SpreadsheetDocument relies on the file being available for read access
-	 * over the whole lifecycle of SpreadsheetDocument.
-	 * </p>
-	 * 
-	 * <p>
-	 * If the resource stream is not a ODF spreadsheet document,
-	 * ClassCastException might be thrown.
-	 * </p>
-	 * 
-	 * @param documentPath
-	 *            - the path from where the document can be loaded
-	 * @return the spreadsheet document from the given path or NULL if the media
-	 *         type is not supported by SIMPLE.
-	 * @throws java.lang.Exception
-	 *             - if the document could not be created.
-	 */
-	public static SpreadsheetDocument loadDocument(String documentPath) throws Exception {
-		return (SpreadsheetDocument) Document.loadDocument(documentPath);
-	}
-
-	/**
-	 * Creates an SpreadsheetDocument from the OpenDocument provided by a File.
-	 * 
-	 * <p>
-	 * SpreadsheetDocument relies on the file being available for read access
-	 * over the whole lifecycle of SpreadsheetDocument.
-	 * </p>
-	 * 
-	 * <p>
-	 * If the resource stream is not a ODF spreadsheet document,
-	 * ClassCastException might be thrown.
-	 * </p>
-	 * 
-	 * @param file
-	 *            - a file representing the ODF spreadsheet document.
-	 * @return the spreadsheet document created from the given File
-	 * @throws java.lang.Exception
-	 *             - if the document could not be created.
-	 */
-	public static SpreadsheetDocument loadDocument(File file) throws Exception {
-		return (SpreadsheetDocument) Document.loadDocument(file);
-	}
-
-	/**
-	 * Get the content root of a spreadsheet document.
-	 * 
-	 * @return content root, representing the office:spreadsheet tag
-	 * @throws Exception
-	 *             if the file DOM could not be created.
-	 */
-	@Override
-	public OfficeSpreadsheetElement getContentRoot() throws Exception {
-		return super.getContentRoot(OfficeSpreadsheetElement.class);
-	}
-
-	/**
-	 * Changes the document to the given mediatype. This method can only be used
-	 * to convert a document to a related mediatype, e.g. template.
-	 * 
-	 * @param mediaType
-	 *            the related ODF mimetype
-	 */
-	public void changeMode(OdfMediaType mediaType) {
-		setOdfMediaType(mediaType.mMediaType);
-	}
-
-	/**
-	 * Retrieves sheet by index.
-	 * 
-	 * @param index
-	 *            the index of the retrieved sheet, which starts from 0. If the
-	 *            index value is out of range (index >= sheet count or index <
-	 *            0), this method would return <code>null</code>.
-	 * @since 0.6
-	 */
-	public Table getSheetByIndex(int index) {
-		if (index < 0) {
-			return null;
-		}
-		int count = 0;
-		try {
-			OfficeSpreadsheetElement spreadsheetElement = getContentRoot();
-			Node child = spreadsheetElement.getFirstChild();
-			while ((child != null) && (count <= index)) {
-				if (child instanceof TableTableElement) {
-					if (count == index) {
-						return getTableBuilder().getTableInstance((TableTableElement) child);
-					} else {
-						count++;
-					}
-				}
-				child = child.getNextSibling();
-			}
-		} catch (Exception e) {
-			Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return null;
-	}
-	
-	/**
-	 * Retrieves sheet by name.
-	 * 
-	 * @param name
-	 *            the name of the retrieved sheet.
-	 * @since 0.6
-	 */
-	public Table getSheetByName(String name) {
-		return getTableByName(name);
-	}
-
-	
-	/**
-	 * Adds a new blank sheet with the specified <code>name</code> to this
-	 * document.
-	 * 
-	 * @param name
-	 *            the name of the new sheet.
-	 * @return added sheet.
-	 * @since 0.6
-	 */
-	public Table appendSheet(String name) {
-		Table newTable = addTable();
-		newTable.setTableName(name);
-		return newTable;
-	}
-
-	/**
-	 * Adds a new sheet with data from existing table.
-	 * <p>
-	 * NOTE: This method copies data from existing table, including linked
-	 * resources and styles, if the source table is not in the target document.
-	 * If these data has dependencies to other data of the source document, the
-	 * data dependencies will not be copied. For example, document A has two
-	 * sheets, "Sheet1" and "Sheet2". In "Sheet2", there is a cell with formula,
-	 * "=sum(Sheet1.A1:Sheet1.A10)". After copy the data of "Sheet2" to the new
-	 * sheet in document B, the result of this formula would be different or
-	 * even invalid in document B.
-	 * 
-	 * @param refTable
-	 *            the reference table, which is the data source of the new
-	 *            sheet.
-	 * @param name
-	 *            the name of the new sheet.
-	 * @return added sheet.
-	 * @since 0.6
-	 */
-	public Table appendSheet(Table refTable, String name) {
-		TableTableElement refTableElement = refTable.getOdfElement();
-		try {
-			OdfContentDom contentDom = getContentDom();
-			TableTableElement newTableEle = (TableTableElement) (refTableElement.cloneNode(true));
-			// not in a same document
-			if (refTableElement.getOwnerDocument() != contentDom) {
-				Document ownerDocument = refTable.getOwnerDocument();
-				copyLinkedRefInBatch(newTableEle, ownerDocument);
-				copyForeignStyleRef(newTableEle, ownerDocument);
-				newTableEle = (TableTableElement) cloneForeignElement(newTableEle, contentDom, true);
-			}
-			updateNames(newTableEle);
-			updateXMLIds(newTableEle);
-			getTableContainerElement().appendChild(newTableEle);
-			Table tableInstance = getTableBuilder().getTableInstance(newTableEle);
-			tableInstance.setTableName(name);
-			return tableInstance;
-		} catch (Exception e) {
-			Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return null;
-	}
-	
-	/**
-	 * Inserts a new blank sheet before the reference index.
-	 * 
-	 * @param before
-	 *            the reference index, which starts from 0. If the index value
-	 *            is out of range (index >= sheet count or index < 0), this
-	 *            method would return <code>null</code>.
-	 * @return inserted sheet.
-	 * @since 0.6
-	 */
-	public Table insertSheet(int before) {
-		if (before < 0) {
-			return null;
-		}
-		int count = 0;
-		try {
-			OfficeSpreadsheetElement spreadsheetElement = getContentRoot();
-			Node child = spreadsheetElement.getFirstChild();
-			while ((child != null) && (count <= before)) {
-				if (child instanceof TableTableElement) {
-					if (count == before) {
-						Table table = getTableBuilder().newTable();
-						getContentRoot().insertBefore(table.getOdfElement(), child);
-						return table;
-					} else {
-						count++;
-					}
-				}
-				child = child.getNextSibling();
-			}
-		} catch (Exception e) {
-			Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return null;
-	}
-	
-	/**
-	 * Inserts a new sheet with data from existing table.
-	 * 
-	 * <p>
-	 * NOTE: This method copies data from existing table, including linked
-	 * resources and styles, if the source table is not in the target document.
-	 * If these data has dependencies to other data of the source document, the
-	 * data dependencies will not be copied. For example, document A has two
-	 * sheets, "Sheet1" and "Sheet2". In "Sheet2", there is a cell with formula,
-	 * "=sum(Sheet1.A1:Sheet1.A10)". After copy the data of "Sheet2" to the new
-	 * sheet in document B, the result of this formula would be different or
-	 * even invalid in document B.
-	 * 
-	 * @param refTable
-	 *            the reference table, which is the data source of the new
-	 *            sheet.
-	 * @param before
-	 *            the reference index, which starts from 0 and new sheet would
-	 *            be inserted before it. If the index value is out of range
-	 *            (index >= sheet count or index < 0), this method would return
-	 *            <code>null</code>.
-	 * @return inserted sheet.
-	 * @since 0.6
-	 */
-	public Table insertSheet(Table refTable, int before) {
-		if (before < 0) {
-			return null;
-		}
-		int count = 0;
-		try {
-			OfficeSpreadsheetElement spreadsheetElement = getContentRoot();
-			Node child = spreadsheetElement.getFirstChild();
-			while ((child != null) && (count <= before)) {
-				if (child instanceof TableTableElement) {
-					if (count == before) {
-						TableTableElement refTableElement = refTable.getOdfElement();
-						try {
-							OdfContentDom contentDom = getContentDom();
-							TableTableElement newTableEle = (TableTableElement) (refTableElement.cloneNode(true));
-							//foreign node not in a same document
-							if (refTableElement.getOwnerDocument() != contentDom) {
-								Document ownerDocument = refTable.getOwnerDocument();
-								copyLinkedRefInBatch(newTableEle, ownerDocument);
-								copyForeignStyleRef(newTableEle, ownerDocument);
-								newTableEle = (TableTableElement) cloneForeignElement(newTableEle, contentDom, true);
-							}
-							updateNames(newTableEle);
-							updateXMLIds(newTableEle);
-							newTableEle.setTableNameAttribute(getUniqueSheetName(this));
-							getContentRoot().insertBefore(newTableEle, child);
-							return getTableBuilder().getTableInstance(newTableEle);
-						} catch (Exception e) {
-							Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-						}
-					} else {
-						count++;
-					}
-				}
-				child = child.getNextSibling();
-			}
-		} catch (Exception e) {
-			Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return null;
-	}
-
-	/**
-	 * Removes the sheet in the specified <code>index</code>.
-	 * 
-	 * @param index
-	 *            the index of the removed sheet, which starts from 0. If the
-	 *            index value is out of range (index >= sheet count or index <
-	 *            0), this method would do nothing.
-	 * @since 0.6
-	 */
-	public void removeSheet(int index) {
-		if (index < 0) {
-			return;
-		}
-		int count = 0;
-		try {
-			OfficeSpreadsheetElement spreadsheetElement = getContentRoot();
-			Node child = spreadsheetElement.getFirstChild();
-			while ((child != null) && (count <= index)) {
-				if (child instanceof TableTableElement) {
-					if (count == index) {
-						spreadsheetElement.removeChild(child);
-						return;
-					} else {
-						count++;
-					}
-				}
-				child = child.getNextSibling();
-			}
-		} catch (Exception e) {
-			Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-		}
-	}
-
-	/**
-	 * Returns the sheet count of this document.
-	 * 
-	 * @return the sheet count of this document.
-	 * @since 0.6
-	 */
-	public int getSheetCount() {
-		int count = 0;
-		try {
-			OfficeSpreadsheetElement spreadsheetElement = getContentRoot();
-			Node child = spreadsheetElement.getFirstChild();
-			while (child != null) {
-				if (child instanceof TableTableElement) {
-					count++;
-				}
-				child = child.getNextSibling();
-			}
-		} catch (Exception e) {
-			Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return count;
-	}
-	
-	public OdfElement getTableContainerElement() {
-		return getTableContainerImpl().getTableContainerElement();
-	}
-	
-	/**
-	 * Creates a new Chart for this spreadsheet document.
-	 * 
-	 * @param title
-	 *            chart title.
-	 * @param dataset
-	 *            chart data set.
-	 * @param rect
-	 *            chart rectangle.
-	 * @return the created chart.
-	 * 
-	 * @since 0.6
-	 */
-	public Chart createChart(String title, DataSet dataset, Rectangle rect) {
-		return getChartContainerImpl().createChart(title, dataset, rect);
-	}
-	
-	/**
-	 * Creates a new Chart for this spreadsheet document.
-	 * 
-	 * @param title
-	 *            chart title.
-	 * @param document
-	 *            the data source spreadsheet document.
-	 * @param cellRangeAddr
-	 *            the cell range address list which is used as chart data set.
-	 * @param firstRowAsLabel
-	 *            whether uses first row as label.
-	 * @param firstColumnAsLabel
-	 *            whether uses first column as label.
-	 * @param rowAsDataSeries
-	 *            whether uses data as series.
-	 * @param rect
-	 *            chart rectangle.
-	 * @return the created chart.
-	 * 
-	 * @since 0.6
-	 */
-	public Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,
-			boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect) {
-		return getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,
-				rowAsDataSeries, rect);
-	}
-	
-	/**
-	 * Creates a new Chart for this spreadsheet document.
-	 * 
-	 * @param title
-	 *            chart rectangle.
-	 * @param labels
-	 *            label strings
-	 * @param legends
-	 *            legend strings
-	 * @param data
-	 *            chart data set.
-	 * @param rect
-	 *            chart rectangle.
-	 * @return the created chart.
-	 * 
-	 * @since 0.6
-	 */
-	public Chart createChart(String title, String[] labels, String[] legends, double[][] data, Rectangle rect) {
-		return getChartContainerImpl().createChart(title, labels, legends, data, rect);
-	}
-	
-	/**
-	 * Creates a new Chart for this spreadsheet document.
-	 * 
-	 * @param title
-	 *            chart rectangle.
-	 * @param document
-	 *            the data source spreadsheet document.
-	 * @param cellRangeAddr
-	 *            the cell range list to be used as chart data.
-	 * @param firstRowAsLabel
-	 *            whether use first row as label.
-	 * @param firstColumnAsLabel
-	 *            whether use first column as label.
-	 * @param rowAsDataSeries
-	 *            whether use row as data series.
-	 * @param rect
-	 *            chart rectangle.
-	 * @param cell
-	 *            the position cell where the new chart is inserted.
-	 * @return the created chart.
-	 * 
-	 * @since 0.6
-	 */
-	public Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,
-			boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect, Cell cell) {
-		return getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,
-				rowAsDataSeries, rect, cell);
-	}
-	
-	public void deleteChartById(String chartId) {
-		getChartContainerImpl().deleteChartById(chartId);
-	}
-
-	public void deleteChartByTitle(String title) {
-		getChartContainerImpl().deleteChartByTitle(title);
-	}
-
-	public Chart getChartById(String chartId) {
-		return getChartContainerImpl().getChartById(chartId);
-	}
-
-	public List<Chart> getChartByTitle(String title) {
-		return getChartContainerImpl().getChartByTitle(title);
-	}
-
-	public int getChartCount() {
-		return getChartContainerImpl().getChartCount();
-	}
-	
-	private static String getUniqueSheetName(TableContainer container) {
-		List<Table> tableList = container.getTableList();
-		boolean notUnique = true;
-		String tablename = "Sheet" + (tableList.size() + 1);
-		while (notUnique) {
-			notUnique = false;
-			for (int i = 0; i < tableList.size(); i++) {
-				if (tableList.get(i).getTableName() != null) {
-					if (tableList.get(i).getTableName().equalsIgnoreCase(tablename)) {
-						notUnique = true;
-						break;
-					}
-				}
-			}
-			if (notUnique) {
-				tablename = tablename + Math.round(Math.random() * 10);
-			}
-		}
-		return tablename;
-	}
-	
-	private ChartContainerImpl getChartContainerImpl() {
-		if (chartContainerImpl == null) {
-			chartContainerImpl = new ChartContainerImpl(this);
-		}
-		return chartContainerImpl;
-	}
-	
-	private class ChartContainerImpl extends AbstractChartContainer {
-		SpreadsheetDocument sdoc;
-		DrawFrameElement drawFrame;
-		protected ChartContainerImpl(Document doc) {
-			super(doc);
-			sdoc = (SpreadsheetDocument) doc;
-		}
-
-		protected DrawFrameElement getChartFrame() throws Exception {
-			OdfContentDom contentDom2 = sdoc.getContentDom();
-			DrawFrameElement drawFrame = contentDom2.newOdfElement(DrawFrameElement.class);
-			TableTableCellElement lastCell = (TableTableCellElement) contentDom2.getXPath().evaluate(
-					"//table:table-cell[last()]", contentDom2, XPathConstants.NODE);
-			lastCell.appendChild(drawFrame);
-			drawFrame.removeAttribute("text:anchor-type");
-			this.drawFrame = drawFrame;
-			return drawFrame;
-		}
-		
-		private Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,
-				boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect, Cell cell) {
-			Chart chart = getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,
-					rowAsDataSeries, rect);
-			cell.getOdfElement().appendChild(this.drawFrame);
-			return chart;
-		}
-	}
-}
+/**

+ * **********************************************************************

+ *

+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER

+ *

+ * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.

+ *

+ * Use is subject to license terms.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy

+ * of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also

+ * obtain a copy of the License at http://odftoolkit.org/docs/license.txt

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ *

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ *

+ ***********************************************************************

+ */

+package org.odftoolkit.simple;

+

+import java.awt.Rectangle;

+import java.io.File;

+import java.io.InputStream;

+import java.util.List;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+import javax.xml.xpath.XPathConstants;

+import org.odftoolkit.odfdom.dom.OdfContentDom;

+import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;

+import org.odftoolkit.odfdom.dom.element.office.OfficeSpreadsheetElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableElement;

+import org.odftoolkit.odfdom.pkg.MediaType;

+import org.odftoolkit.odfdom.pkg.OdfElement;

+import org.odftoolkit.odfdom.pkg.OdfPackage;

+import org.odftoolkit.odfdom.type.CellRangeAddressList;

+import org.odftoolkit.simple.chart.AbstractChartContainer;

+import org.odftoolkit.simple.chart.Chart;

+import org.odftoolkit.simple.chart.ChartContainer;

+import org.odftoolkit.simple.chart.DataSet;

+import org.odftoolkit.simple.table.Cell;

+import org.odftoolkit.simple.table.RepeatedNumberUtils;

+import org.odftoolkit.simple.table.Table;

+import org.odftoolkit.simple.table.TableContainer;

+import org.w3c.dom.Node;

+

+/**

+ * This class represents an empty ODF spreadsheet document.

+ *

+ */

+public class SpreadsheetDocument extends Document implements ChartContainer {

+

+    private static final String EMPTY_SPREADSHEET_DOCUMENT_PATH = "/OdfSpreadsheetDocument.ods";

+    static final Resource EMPTY_SPREADSHEET_DOCUMENT_RESOURCE = new Resource(EMPTY_SPREADSHEET_DOCUMENT_PATH);

+    private ChartContainerImpl chartContainerImpl;

+

+    /**

+     * This enum contains all possible media types of SpreadsheetDocument

+     * documents.

+     */

+    public enum OdfMediaType implements MediaType {

+

+        SPREADSHEET(Document.OdfMediaType.SPREADSHEET), SPREADSHEET_TEMPLATE(Document.OdfMediaType.SPREADSHEET_TEMPLATE);

+        private final Document.OdfMediaType mMediaType;

+

+        OdfMediaType(Document.OdfMediaType mediaType) {

+            this.mMediaType = mediaType;

+        }

+

+        /**

+         * @return the mediatype of this document

+         */

+        public String getMediaTypeString() {

+            return mMediaType.getMediaTypeString();

+        }

+

+        /**

+         * @return the ODF filesuffix of this document

+         */

+        public String getSuffix() {

+            return mMediaType.getSuffix();

+        }

+

+        /**

+         *

+         * @param mediaType string defining an ODF document

+         * @return the according OdfMediatype encapuslating the given string and

+         * the suffix

+         */

+        public static Document.OdfMediaType getOdfMediaType(String mediaType) {

+            return Document.OdfMediaType.getOdfMediaType(mediaType);

+        }

+    }

+

+    /**

+     * Creates an empty spreadsheet document.

+     *

+     * @return ODF spreadsheet document based on a default template

+     *

+     * @throws java.lang.Exception - if the document could not be created

+     */

+    public static SpreadsheetDocument newSpreadsheetDocument() throws Exception {

+        return (SpreadsheetDocument) Document.loadTemplate(EMPTY_SPREADSHEET_DOCUMENT_RESOURCE,

+            Document.OdfMediaType.SPREADSHEET);

+    }

+

+    /**

+     * Creates an empty spreadsheet template.

+     *

+     * @return ODF spreadsheet template based on a default

+     * @throws java.lang.Exception - if the template could not be created

+     */

+    public static SpreadsheetDocument newSpreadsheetTemplateDocument() throws Exception {

+        SpreadsheetDocument doc = (SpreadsheetDocument) Document.loadTemplate(EMPTY_SPREADSHEET_DOCUMENT_RESOURCE,

+            Document.OdfMediaType.SPREADSHEET_TEMPLATE);

+        doc.changeMode(OdfMediaType.SPREADSHEET_TEMPLATE);

+        return doc;

+    }

+

+    /**

+     * To avoid data duplication a new document is only created, if not already

+     * opened. A document is cached by this constructor using the internalpath

+     * as key.

+     */

+    protected SpreadsheetDocument(OdfPackage pkg, String internalPath, SpreadsheetDocument.OdfMediaType odfMediaType) {

+        super(pkg, internalPath, odfMediaType.mMediaType);

+    }

+

+    /**

+     * Creates an SpreadsheetDocument from the OpenDocument provided by a

+     * resource Stream.

+     *

+     * <p>

+     * Since an InputStream does not provide the arbitrary (non sequentiell)

+     * read access needed by SpreadsheetDocument, the InputStream is cached.

+     * This usually takes more time compared to the other createInternalDocument

+     * methods. An advantage of caching is that there are no problems

+     * overwriting an input file.

+     * </p>

+     *

+     * <p>

+     * If the resource stream is not a ODF spreadsheet document,

+     * ClassCastException might be thrown.

+     * </p>

+     *

+     * @param inputStream - the InputStream of the ODF spreadsheet document.

+     * @return the spreadsheet document created from the given InputStream

+     * @throws java.lang.Exception - if the document could not be created.

+     */

+    public static SpreadsheetDocument loadDocument(InputStream inputStream) throws Exception {

+        SpreadsheetDocument document = (SpreadsheetDocument) Document.loadDocument(inputStream);

+        removeDummyCellsFromDocument(document);

+        return document;

+    }

+

+    /**

+     * Loads an SpreadsheetDocument from the provided path.

+     *

+     * <p>

+     * SpreadsheetDocument relies on the file being available for read access

+     * over the whole lifecycle of SpreadsheetDocument.

+     * </p>

+     *

+     * <p>

+     * If the resource stream is not a ODF spreadsheet document,

+     * ClassCastException might be thrown.

+     * </p>

+     *

+     * @param documentPath - the path from where the document can be loaded

+     * @return the spreadsheet document from the given path or NULL if the media

+     * type is not supported by SIMPLE.

+     * @throws java.lang.Exception - if the document could not be created.

+     */

+    public static SpreadsheetDocument loadDocument(String documentPath) throws Exception {

+        SpreadsheetDocument document = (SpreadsheetDocument) Document.loadDocument(documentPath);

+        removeDummyCellsFromDocument(document);

+        return document;

+    }

+

+    /**

+     * Creates an SpreadsheetDocument from the OpenDocument provided by a File.

+     *

+     * <p>

+     * SpreadsheetDocument relies on the file being available for read access

+     * over the whole lifecycle of SpreadsheetDocument.

+     * </p>

+     *

+     * <p>

+     * If the resource stream is not a ODF spreadsheet document,

+     * ClassCastException might be thrown.

+     * </p>

+     *

+     * @param file - a file representing the ODF spreadsheet document.

+     * @return the spreadsheet document created from the given File

+     * @throws java.lang.Exception - if the document could not be created.

+     */

+    public static SpreadsheetDocument loadDocument(File file) throws Exception {

+        SpreadsheetDocument document = (SpreadsheetDocument) Document.loadDocument(file);

+        removeDummyCellsFromDocument(document);

+        return document;

+    }

+

+    /**

+     * Get the content root of a spreadsheet document.

+     *

+     * @return content root, representing the office:spreadsheet tag

+     * @throws Exception if the file DOM could not be created.

+     */

+    @Override

+    public OfficeSpreadsheetElement getContentRoot() throws Exception {

+        return super.getContentRoot(OfficeSpreadsheetElement.class);

+    }

+

+    /**

+     * Changes the document to the given mediatype. This method can only be used

+     * to convert a document to a related mediatype, e.g. template.

+     *

+     * @param mediaType the related ODF mimetype

+     */

+    public void changeMode(OdfMediaType mediaType) {

+        setOdfMediaType(mediaType.mMediaType);

+    }

+

+    /**

+     * Retrieves sheet by index.

+     *

+     * @param index the index of the retrieved sheet, which starts from 0. If

+     * the index value is out of range (index >= sheet count or index < 0), this

+     * method would return <code>null</code>. @since 0.6

+     */

+    public Table getSheetByIndex(int index) {

+        if (index < 0) {

+            return null;

+        }

+        int count = 0;

+        try {

+            OfficeSpreadsheetElement spreadsheetElement = getContentRoot();

+            Node child = spreadsheetElement.getFirstChild();

+            while ((child != null) && (count <= index)) {

+                if (child instanceof TableTableElement) {

+                    if (count == index) {

+                        return getTableBuilder().getTableInstance((TableTableElement) child);

+                    } else {

+                        count++;

+                    }

+                }

+                child = child.getNextSibling();

+            }

+        } catch (Exception e) {

+            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+        }

+        return null;

+    }

+

+    /**

+     * Retrieves sheet by name.

+     *

+     * @param name the name of the retrieved sheet.

+     * @since 0.6

+     */

+    public Table getSheetByName(String name) {

+        return getTableByName(name);

+    }

+

+    /**

+     * Adds a new blank sheet with the specified <code>name</code> to this

+     * document.

+     *

+     * @param name the name of the new sheet.

+     * @return added sheet.

+     * @since 0.6

+     */

+    public Table appendSheet(String name) {

+        Table newTable = addTable();

+        newTable.setTableName(name);

+        return newTable;

+    }

+

+    /**

+     * Adds a new sheet with data from existing table.

+     * <p>

+     * NOTE: This method copies data from existing table, including linked

+     * resources and styles, if the source table is not in the target document.

+     * If these data has dependencies to other data of the source document, the

+     * data dependencies will not be copied. For example, document A has two

+     * sheets, "Sheet1" and "Sheet2". In "Sheet2", there is a cell with formula,

+     * "=sum(Sheet1.A1:Sheet1.A10)". After copy the data of "Sheet2" to the new

+     * sheet in document B, the result of this formula would be different or

+     * even invalid in document B.

+     *

+     * @param refTable the reference table, which is the data source of the new

+     * sheet.

+     * @param name the name of the new sheet.

+     * @return added sheet.

+     * @since 0.6

+     */

+    public Table appendSheet(Table refTable, String name) {

+        TableTableElement refTableElement = refTable.getOdfElement();

+        try {

+            OdfContentDom contentDom = getContentDom();

+            TableTableElement newTableEle = (TableTableElement) (refTableElement.cloneNode(true));

+            // not in a same document

+            if (refTableElement.getOwnerDocument() != contentDom) {

+                Document ownerDocument = refTable.getOwnerDocument();

+                copyLinkedRefInBatch(newTableEle, ownerDocument);

+                copyForeignStyleRef(newTableEle, ownerDocument);

+                newTableEle = (TableTableElement) cloneForeignElement(newTableEle, contentDom, true);

+            }

+            updateNames(newTableEle);

+            updateXMLIds(newTableEle);

+            getTableContainerElement().appendChild(newTableEle);

+            Table tableInstance = getTableBuilder().getTableInstance(newTableEle);

+            tableInstance.setTableName(name);

+            return tableInstance;

+        } catch (Exception e) {

+            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+        }

+        return null;

+    }

+

+    /**

+     * Inserts a new blank sheet before the reference index.

+     *

+     * @param before the reference index, which starts from 0. If the index

+     * value is out of range (index >= sheet count or index < 0), this method

+     * would return <code>null</code>. @return inserted sheet. @since 0.6

+     */

+    public Table insertSheet(int before) {

+        if (before < 0) {

+            return null;

+        }

+        int count = 0;

+        try {

+            OfficeSpreadsheetElement spreadsheetElement = getContentRoot();

+            Node child = spreadsheetElement.getFirstChild();

+            while ((child != null) && (count <= before)) {

+                if (child instanceof TableTableElement) {

+                    if (count == before) {

+                        Table table = getTableBuilder().newTable();

+                        getContentRoot().insertBefore(table.getOdfElement(), child);

+                        return table;

+                    } else {

+                        count++;

+                    }

+                }

+                child = child.getNextSibling();

+            }

+        } catch (Exception e) {

+            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+        }

+        return null;

+    }

+

+    /**

+     * Inserts a new sheet with data from existing table.

+     *

+     * <p>

+     * NOTE: This method copies data from existing table, including linked

+     * resources and styles, if the source table is not in the target document.

+     * If these data has dependencies to other data of the source document, the

+     * data dependencies will not be copied. For example, document A has two

+     * sheets, "Sheet1" and "Sheet2". In "Sheet2", there is a cell with formula,

+     * "=sum(Sheet1.A1:Sheet1.A10)". After copy the data of "Sheet2" to the new

+     * sheet in document B, the result of this formula would be different or

+     * even invalid in document B.

+     *

+     * @param refTable the reference table, which is the data source of the new

+     * sheet.

+     * @param before the reference index, which starts from 0 and new sheet

+     * would be inserted before it. If the index value is out of range (index >=

+     * sheet count or index < 0), this method would return <code>null</code>.

+     * @return inserted sheet.

+     * @since 0.6

+     */

+    public Table insertSheet(Table refTable, int before) {

+        if (before < 0) {

+            return null;

+        }

+        int count = 0;

+        try {

+            OfficeSpreadsheetElement spreadsheetElement = getContentRoot();

+            Node child = spreadsheetElement.getFirstChild();

+            while ((child != null) && (count <= before)) {

+                if (child instanceof TableTableElement) {

+                    if (count == before) {

+                        TableTableElement refTableElement = refTable.getOdfElement();

+                        try {

+                            OdfContentDom contentDom = getContentDom();

+                            TableTableElement newTableEle = (TableTableElement) (refTableElement.cloneNode(true));

+                            //foreign node not in a same document

+                            if (refTableElement.getOwnerDocument() != contentDom) {

+                                Document ownerDocument = refTable.getOwnerDocument();

+                                copyLinkedRefInBatch(newTableEle, ownerDocument);

+                                copyForeignStyleRef(newTableEle, ownerDocument);

+                                newTableEle = (TableTableElement) cloneForeignElement(newTableEle, contentDom, true);

+                            }

+                            updateNames(newTableEle);

+                            updateXMLIds(newTableEle);

+                            newTableEle.setTableNameAttribute(getUniqueSheetName(this));

+                            getContentRoot().insertBefore(newTableEle, child);

+                            return getTableBuilder().getTableInstance(newTableEle);

+                        } catch (Exception e) {

+                            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+                        }

+                    } else {

+                        count++;

+                    }

+                }

+                child = child.getNextSibling();

+            }

+        } catch (Exception e) {

+            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+        }

+        return null;

+    }

+

+    /**

+     * Removes the sheet in the specified <code>index</code>.

+     *

+     * @param index the index of the removed sheet, which starts from 0. If the

+     * index value is out of range (index >= sheet count or index < 0), this

+     * method would do nothing. @since 0.6

+     */

+    public void removeSheet(int index) {

+        if (index < 0) {

+            return;

+        }

+        int count = 0;

+        try {

+            OfficeSpreadsheetElement spreadsheetElement = getContentRoot();

+            Node child = spreadsheetElement.getFirstChild();

+            while ((child != null) && (count <= index)) {

+                if (child instanceof TableTableElement) {

+                    if (count == index) {

+                        spreadsheetElement.removeChild(child);

+                        return;

+                    } else {

+                        count++;

+                    }

+                }

+                child = child.getNextSibling();

+            }

+        } catch (Exception e) {

+            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+        }

+    }

+

+    /**

+     * Returns the sheet count of this document.

+     *

+     * @return the sheet count of this document.

+     * @since 0.6

+     */

+    public int getSheetCount() {

+        int count = 0;

+        try {

+            OfficeSpreadsheetElement spreadsheetElement = getContentRoot();

+            Node child = spreadsheetElement.getFirstChild();

+            while (child != null) {

+                if (child instanceof TableTableElement) {

+                    count++;

+                }

+                child = child.getNextSibling();

+            }

+        } catch (Exception e) {

+            Logger.getLogger(SpreadsheetDocument.class.getName()).log(Level.SEVERE, null, e);

+        }

+        return count;

+    }

+

+    public OdfElement getTableContainerElement() {

+        return getTableContainerImpl().getTableContainerElement();

+    }

+

+    /**

+     * Creates a new Chart for this spreadsheet document.

+     *

+     * @param title chart title.

+     * @param dataset chart data set.

+     * @param rect chart rectangle.

+     * @return the created chart.

+     *

+     * @since 0.6

+     */

+    public Chart createChart(String title, DataSet dataset, Rectangle rect) {

+        return getChartContainerImpl().createChart(title, dataset, rect);

+    }

+

+    /**

+     * Creates a new Chart for this spreadsheet document.

+     *

+     * @param title chart title.

+     * @param document the data source spreadsheet document.

+     * @param cellRangeAddr the cell range address list which is used as chart

+     * data set.

+     * @param firstRowAsLabel whether uses first row as label.

+     * @param firstColumnAsLabel whether uses first column as label.

+     * @param rowAsDataSeries whether uses data as series.

+     * @param rect chart rectangle.

+     * @return the created chart.

+     *

+     * @since 0.6

+     */

+    public Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,

+        boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect) {

+        return getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,

+            rowAsDataSeries, rect);

+    }

+

+    /**

+     * Creates a new Chart for this spreadsheet document.

+     *

+     * @param title chart rectangle.

+     * @param labels label strings

+     * @param legends legend strings

+     * @param data chart data set.

+     * @param rect chart rectangle.

+     * @return the created chart.

+     *

+     * @since 0.6

+     */

+    public Chart createChart(String title, String[] labels, String[] legends, double[][] data, Rectangle rect) {

+        return getChartContainerImpl().createChart(title, labels, legends, data, rect);

+    }

+

+    /**

+     * Creates a new Chart for this spreadsheet document.

+     *

+     * @param title chart rectangle.

+     * @param document the data source spreadsheet document.

+     * @param cellRangeAddr the cell range list to be used as chart data.

+     * @param firstRowAsLabel whether use first row as label.

+     * @param firstColumnAsLabel whether use first column as label.

+     * @param rowAsDataSeries whether use row as data series.

+     * @param rect chart rectangle.

+     * @param cell the position cell where the new chart is inserted.

+     * @return the created chart.

+     *

+     * @since 0.6

+     */

+    public Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,

+        boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect, Cell cell) {

+        return getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,

+            rowAsDataSeries, rect, cell);

+    }

+

+    public void deleteChartById(String chartId) {

+        getChartContainerImpl().deleteChartById(chartId);

+    }

+

+    public void deleteChartByTitle(String title) {

+        getChartContainerImpl().deleteChartByTitle(title);

+    }

+

+    public Chart getChartById(String chartId) {

+        return getChartContainerImpl().getChartById(chartId);

+    }

+

+    public List<Chart> getChartByTitle(String title) {

+        return getChartContainerImpl().getChartByTitle(title);

+    }

+

+    public int getChartCount() {

+        return getChartContainerImpl().getChartCount();

+    }

+

+    private static String getUniqueSheetName(TableContainer container) {

+        List<Table> tableList = container.getTableList();

+        boolean notUnique = true;

+        String tablename = "Sheet" + (tableList.size() + 1);

+        while (notUnique) {

+            notUnique = false;

+            for (int i = 0; i < tableList.size(); i++) {

+                if (tableList.get(i).getTableName() != null) {

+                    if (tableList.get(i).getTableName().equalsIgnoreCase(tablename)) {

+                        notUnique = true;

+                        break;

+                    }

+                }

+            }

+            if (notUnique) {

+                tablename = tablename + Math.round(Math.random() * 10);

+            }

+        }

+        return tablename;

+    }

+

+    private ChartContainerImpl getChartContainerImpl() {

+        if (chartContainerImpl == null) {

+            chartContainerImpl = new ChartContainerImpl(this);

+        }

+        return chartContainerImpl;

+    }

+

+    private class ChartContainerImpl extends AbstractChartContainer {

+

+        SpreadsheetDocument sdoc;

+        DrawFrameElement drawFrame;

+

+        protected ChartContainerImpl(Document doc) {

+            super(doc);

+            sdoc = (SpreadsheetDocument) doc;

+        }

+

+        protected DrawFrameElement getChartFrame() throws Exception {

+            OdfContentDom contentDom2 = sdoc.getContentDom();

+            DrawFrameElement drawFrame = contentDom2.newOdfElement(DrawFrameElement.class);

+            TableTableCellElement lastCell = (TableTableCellElement) contentDom2.getXPath().evaluate(

+                "//table:table-cell[last()]", contentDom2, XPathConstants.NODE);

+            lastCell.appendChild(drawFrame);

+            drawFrame.removeAttribute("text:anchor-type");

+            this.drawFrame = drawFrame;

+            return drawFrame;

+        }

+

+        private Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,

+            boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect, Cell cell) {

+            Chart chart = getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,

+                rowAsDataSeries, rect);

+            cell.getOdfElement().appendChild(this.drawFrame);

+            return chart;

+        }

+    }

+

+    /**

+     * @see RepeatedNumberUtils#removeDummyCellsFromTable(Table)

+     */

+    private static void removeDummyCellsFromDocument(SpreadsheetDocument document) {

+        int sheetCount = document.getSheetCount();

+        for (int n = 0; n < sheetCount; n++) {

+            RepeatedNumberUtils.removeDummyCellsFromTable(document.getSheetByIndex(n));

+        }

+    }

+}

diff --git a/simple/src/main/java/org/odftoolkit/simple/table/RepeatedNumberUtils.java b/simple/src/main/java/org/odftoolkit/simple/table/RepeatedNumberUtils.java
new file mode 100644
index 0000000..4f6ea2d
--- /dev/null
+++ b/simple/src/main/java/org/odftoolkit/simple/table/RepeatedNumberUtils.java
@@ -0,0 +1,248 @@
+/*

+Licensed to the Apache Software Foundation (ASF) under one

+or more contributor license agreements.  See the NOTICE file

+distributed with this work for additional information

+regarding copyright ownership.	The ASF licenses this file

+to you under the Apache License, Version 2.0 (the

+"License"); you may not use this file except in compliance

+with the License.  You may obtain a copy of the License at

+

+http://www.apache.org/licenses/LICENSE-2.0

+

+Unless required by applicable law or agreed to in writing,

+software distributed under the License is distributed on an

+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+KIND, either express or implied.	See the License for the

+specific language governing permissions and limitations

+under the License.

+ */

+package org.odftoolkit.simple.table;

+

+import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableColumnElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableColumnsElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableHeaderColumnsElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableHeaderRowsElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableRowElement;

+import org.odftoolkit.odfdom.dom.element.table.TableTableRowsElement;

+import org.w3c.dom.DOMException;

+import org.w3c.dom.Node;

+

+/**

+ * Utility functions to evaluate the "repeated-number"-Attributes of rows,

+ * columns and cells.

+ *

+ * @author raimund

+ */

+public class RepeatedNumberUtils {

+

+    private static final int LIBRE_OFFICE_WORKAROUND_COLCOUNT = 1024;

+    private static final int LIBRE_OFFICE_WORKAROUND_ROWCOUNT = 1048576;

+    private static final int MS_EXCEL_BINARY_WORKAROUND_ROWCOUNT = 32768;

+

+    /**

+     * Remove unused columns and rows from the end of a table.

+     * <p>

+     * This is a workaround for bug ODFTOOLKIT-388. LibreOffice adds two dummy

+     * rows with "number-rows-repeated" attribute to "blow up" the sheet to

+     * {@value #LIBRE_OFFICE_WORKAROUND_ROWCOUNT} rows as needed by MS Excel

+     * 2010.

+     * <p>

+     * Files converted from MS Excel binary format (*.xls) contain a similar

+     * dummy row to "blow up" the sheet up to

+     * {@value #MS_EXCEL_BINARY_WORKAROUND_ROWCOUNT} rows.

+     * <p>

+     * Same is done for columns - one dummy column is added to get a whole

+     * column count of {@value #LIBRE_OFFICE_WORKAROUND_COLCOUNT}.

+     *

+     * @param table Table to clean.

+     */

+    public static void removeDummyCellsFromTable(Table table) {

+        // First analyze columns and rows; e. g. count them

+        int wholeColumnCount = 0;

+        int wholeRowCount = 0;

+        int maxCellCount = 0;

+        TableTableColumnElement lastColumnNode = null;

+        TableTableRowElement msExcelDummyRow = null;

+        for (Node node : new DomNodeList(table.getOdfElement().getChildNodes())) {

+            // TODO: how about <table:table-column-group>

+            if (node instanceof TableTableHeaderColumnsElement) {

+                wholeColumnCount += _getHeaderColumnCount((TableTableHeaderColumnsElement) node);

+                lastColumnNode = null;

+

+            } else if (node instanceof TableTableColumnsElement) {

+                wholeColumnCount += _getColumnsCount((TableTableColumnsElement) node);

+                lastColumnNode = null;

+

+            } else if (node instanceof TableTableColumnElement) {

+                wholeColumnCount += _getNumberColumnsRepeated((TableTableColumnElement) node);

+                lastColumnNode = (TableTableColumnElement) node;

+            }

+

+            if (node instanceof TableTableHeaderRowsElement) {

+                wholeRowCount += _getHeaderRowCount((TableTableHeaderRowsElement) node);

+                int n = _getCellCountOfRowWithoutDummies(node);

+                maxCellCount = n > maxCellCount ? n : maxCellCount;

+

+            } else if (node instanceof TableTableRowElement) {

+                wholeRowCount += _getNumberRowsRepeated((TableTableRowElement) node);

+                if (wholeRowCount == MS_EXCEL_BINARY_WORKAROUND_ROWCOUNT) {

+                    msExcelDummyRow = (TableTableRowElement) node;

+                }

+                int n = _getCellCountOfRowWithoutDummies(node);

+                maxCellCount = n > maxCellCount ? n : maxCellCount;

+

+            } else if (node instanceof TableTableRowsElement) {

+                for (Node nodeChild : new DomNodeList(node.getChildNodes())) {

+                    if (nodeChild instanceof TableTableRowElement) {

+                        wholeRowCount += _getNumberRowsRepeated((TableTableRowElement) nodeChild);

+                        int n = _getCellCountOfRowWithoutDummies(node);

+                        maxCellCount = n > maxCellCount ? n : maxCellCount;

+                    }

+                }

+            }

+        }

+

+        // Removing the dummy rows at end if needed

+        if (wholeRowCount == LIBRE_OFFICE_WORKAROUND_ROWCOUNT) {

+            boolean done = false;

+            for (int nodeIndex = table.getOdfElement().getChildNodes().getLength() - 1; !done && nodeIndex > 0; nodeIndex--) {

+                Node node = table.getOdfElement().getChildNodes().item(nodeIndex);

+                if (node instanceof TableTableHeaderRowsElement || node instanceof TableTableRowsElement) {

+                    done = true;

+

+                } else if (node instanceof TableTableRowElement) {

+                    if (_isEmptyDummyRow((TableTableRowElement) node)) {

+                        table.getOdfElement().removeChild(node);

+                    } else {

+                        done = true;

+                    }

+                }

+            }

+        }

+

+        // Workaround for files converted from MS Excel

+        if (msExcelDummyRow != null && msExcelDummyRow == table.getOdfElement().getLastChild()) {

+            try {

+                table.getOdfElement().removeChild(msExcelDummyRow);

+            } catch (DOMException e) {

+                // Row may be already removed by code above - just ignore that

+            }

+        }

+

+        // Removing the dummy columns at end if needed.

+        // Warning, we must retain at least one column for each cell,

+        // so it may be needed to just reduce the number-columns-repeated

+        // instead of deleting the cell.

+        if (wholeColumnCount == LIBRE_OFFICE_WORKAROUND_COLCOUNT && lastColumnNode != null) {

+            int n = _getNumberColumnsRepeated(lastColumnNode);

+            if (n > 1) {

+                int realColumnCount = wholeColumnCount - n;

+                if (realColumnCount < maxCellCount) {

+                    lastColumnNode.setTableNumberColumnsRepeatedAttribute(maxCellCount - realColumnCount);

+                } else {

+                    table.getOdfElement().removeChild(lastColumnNode);

+                }

+            }

+        }

+    }

+

+    private static int _getCellCountOfRowWithoutDummies(Node row) {

+        int cellCount = 0;

+        Node lastCell = row.getLastChild();

+        for (Node node : new DomNodeList(row.getChildNodes())) {

+            if (node instanceof TableTableCellElement) {

+                TableTableCellElement cell = (TableTableCellElement) node;

+

+                Integer repCnt = cell.getTableNumberColumnsRepeatedAttribute();

+                if (repCnt == null) {

+                    repCnt = 1;

+                }

+                Integer spanned = cell.getTableNumberColumnsSpannedAttribute();

+                if (spanned == null || spanned == 0) {

+                    spanned = 1;

+                }

+

+                if (node != lastCell || repCnt == 1 || spanned != 1) {

+                    cellCount += repCnt * spanned;

+                }

+            }

+        }

+        return cellCount;

+    }

+

+    private static int _getNumberRowsRepeated(TableTableRowElement rowElement) {

+        int numberRowsRepeated = 0;

+

+        if (rowElement != null) {

+            Integer repCnt = rowElement.getTableNumberRowsRepeatedAttribute();

+            if (repCnt == null) {

+                numberRowsRepeated = 1;

+            } else {

+                numberRowsRepeated = repCnt;

+            }

+        }

+

+        return numberRowsRepeated;

+    }

+

+    static int _getNumberColumnsRepeated(TableTableColumnElement columnElement) {

+        int numberColumnsRepeated = 0;

+

+        if (columnElement != null) {

+            Integer repCnt = columnElement.getTableNumberColumnsRepeatedAttribute();

+            if (repCnt == null) {

+                numberColumnsRepeated = 1;

+            } else {

+                numberColumnsRepeated = repCnt;

+            }

+        }

+

+        return numberColumnsRepeated;

+    }

+

+    private static int _getHeaderColumnCount(TableTableHeaderColumnsElement headers) {

+        int result = 0;

+        if (headers != null) {

+            for (Node n : new DomNodeList(headers.getChildNodes())) {

+                result += _getNumberColumnsRepeated((TableTableColumnElement) n);

+            }

+        }

+        return result;

+    }

+

+    private static int _getColumnsCount(TableTableColumnsElement columns) {

+        int result = 0;

+        if (columns != null) {

+            for (Node n : new DomNodeList(columns.getChildNodes())) {

+                result += _getNumberColumnsRepeated((TableTableColumnElement) n);

+            }

+        }

+        return result;

+    }

+

+    private static int _getHeaderRowCount(TableTableHeaderRowsElement headers) {

+        int result = 0;

+        if (headers != null) {

+            for (Node n : new DomNodeList(headers.getChildNodes())) {

+                if (n instanceof TableTableRowElement) {

+                    result += _getNumberRowsRepeated((TableTableRowElement) n);

+                }

+            }

+        }

+        return result;

+    }

+

+    private static boolean _isEmptyDummyRow(TableTableRowElement row) {

+        boolean dummyRow = true;

+        for (Node child = row.getChildNodes().item(0); dummyRow && child != null; child = child.getNextSibling()) {

+            if (child instanceof TableTableCellElement) {

+                dummyRow = child.getChildNodes().getLength() == 0;

+            } else {

+                dummyRow = false;

+            }

+        }

+

+        return dummyRow;

+    }

+}

diff --git a/simple/src/test/java/org/odftoolkit/simple/SpreadsheetDocumentTest.java b/simple/src/test/java/org/odftoolkit/simple/SpreadsheetDocumentTest.java
index 6f7a244..5f460a1 100644
--- a/simple/src/test/java/org/odftoolkit/simple/SpreadsheetDocumentTest.java
+++ b/simple/src/test/java/org/odftoolkit/simple/SpreadsheetDocumentTest.java
@@ -1,259 +1,284 @@
-/* 
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-
-package org.odftoolkit.simple;
-
-import java.awt.Rectangle;
-import java.io.File;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.odftoolkit.simple.Document.OdfMediaType;
-import org.odftoolkit.simple.chart.Chart;
-import org.odftoolkit.simple.chart.ChartType;
-import org.odftoolkit.simple.chart.DataSet;
-import org.odftoolkit.simple.table.Column;
-import org.odftoolkit.simple.table.Table;
-import org.odftoolkit.simple.utils.ResourceUtilities;
-
-public class SpreadsheetDocumentTest {
-	private static final Logger LOG = Logger.getLogger(SpreadsheetDocumentTest.class.getName());
-	private static final String TEST_FILE = "spreadsheetTestTemplate.ots";
-	
-	@Test
-	public void testGetMediaTypeString() throws Exception{
-		
-		try {
-			String spreadDocPath = ResourceUtilities.getAbsolutePath(TEST_FILE);
-			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(spreadDocPath);
-			Assert.assertNotNull(spreadDoc);
-			OdfMediaType odfMediaType = spreadDoc.getOdfMediaType();
-			Assert.assertEquals(Document.OdfMediaType.SPREADSHEET_TEMPLATE, odfMediaType);
-		} catch (Exception e) {
-			LOG.log(Level.SEVERE, e.getMessage(), e);
-			Assert.fail(e.getMessage());
-		}
-		
-	}
-	
-	@Test
-	public void testGetSuffix() throws Exception{
-		try {
-			String spreadDocPath = ResourceUtilities.getAbsolutePath(TEST_FILE);
-			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(spreadDocPath);
-			Assert.assertNotNull(spreadDoc);
-			OdfMediaType odfMediaType = spreadDoc.getOdfMediaType();
-			String suffix = odfMediaType.getSuffix();
-			Assert.assertEquals("ots", suffix);
-		} catch (Exception e) {
-			LOG.log(Level.SEVERE, e.getMessage(), e);
-			Assert.fail(e.getMessage());
-		}
-	}
-	
-	@Test
-	public void testGetOdfMediaType() throws Exception{
-		
-		try {
-			String spreadDocPath = ResourceUtilities.getAbsolutePath(TEST_FILE);
-			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(spreadDocPath);
-			Assert.assertNotNull(spreadDoc);
-			Document.OdfMediaType odfMediaType = SpreadsheetDocument.OdfMediaType.getOdfMediaType(spreadDoc.getMediaTypeString());
-			Assert.assertEquals("ots", odfMediaType.getSuffix());
-		} catch (Exception e) {
-			LOG.log(Level.SEVERE, e.getMessage(), e);
-			Assert.fail(e.getMessage());
-		}
-	}
-	
-	@Test
-	public void testLoadDocument() throws Exception{
-		try {
-			String filePath = ResourceUtilities.getAbsolutePath(TEST_FILE);
-			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(filePath);
-			Assert.assertNotNull(spreadDoc);
-			Assert.assertEquals(Document.OdfMediaType.SPREADSHEET_TEMPLATE, spreadDoc.getOdfMediaType());
-		} catch (Exception e) {
-			LOG.log(Level.SEVERE, e.getMessage(), e);
-			Assert.fail(e.getMessage());
-		}
-	}
-	
-	@Test
-	public void testLoadDocumentFile() throws Exception{
-		
-		try {
-			String filePath = ResourceUtilities.getAbsolutePath(TEST_FILE);
-			File fileDoc = new File(filePath);
-			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(fileDoc);;
-			Assert.assertNotNull(spreadDoc);
-			Assert.assertEquals(Document.OdfMediaType.SPREADSHEET_TEMPLATE, spreadDoc.getOdfMediaType());
-		} catch (Exception e) {
-			LOG.log(Level.SEVERE, e.getMessage(), e);
-			Assert.fail(e.getMessage());
-		}
-		
-	}
-	
-	@Test
-	public void testCreateChart() throws Exception{
-		try {
-			SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(ResourceUtilities.getAbsolutePath(TEST_FILE));
-			String title = "XXXTitle";
-			String[] lables = new String[]{"spring","summer","autumn","autumn"};
-			String[] legends = new String[]{"hello1","hello2","hello3"};
-			double[][] data = new double[][]{{1.2,2.22,3},{2,3,4},{3,4,5},{4,5,6}};
-			DataSet dataset = new DataSet(lables, legends, data);
-			Rectangle rect = new Rectangle();
-			rect.x = 367;
-			rect.y = 389;
-			rect.width = 379;
-			rect.height = 424;
-			Chart spChart = spDocument.createChart(title, dataset, rect);
-			Assert.assertNotNull(spChart);
-			spChart.setChartType(ChartType.AREA);
-			//save
-			spDocument.save(ResourceUtilities.getTestOutput("Chart_"+TEST_FILE));
-			
-			Assert.assertEquals(dataset, spChart.getChartData());
-			Assert.assertEquals("XXXTitle", spChart.getChartTitle());
-			Assert.assertEquals(ChartType.AREA, spChart.getChartType());
-			
-			LOG.log(Level.INFO,"spChart--> " + spChart);
-			
-		} catch (Exception e) {
-			LOG.log(Level.SEVERE, e.getMessage(), e);
-			Assert.fail(e.getMessage());
-		}
-	}
-	
-	@Test
-	public void testGetSheetByIndex() throws Exception{
-		File file = new File(ResourceUtilities.getAbsolutePath("TestSpreadsheetTable.ods"));
-		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);
-		//index < 0 , Not expected, table ==null
-		Table tablenull = spDocument.getSheetByIndex(-1);
-		Assert.assertTrue((tablenull == null));
-		//index > 0 
-		//index = 0
-		Table tableSheet0 = spDocument.getSheetByIndex(0);
-		Assert.assertTrue((tableSheet0 != null));
-		Assert.assertEquals("Sheet1", tableSheet0.getTableName());
-		Assert.assertEquals(29, tableSheet0.getColumnCount());
-		//index = 1
-		Table tableSheet1 = spDocument.getSheetByIndex(1);
-		Assert.assertTrue((tableSheet1 != null));
-		Assert.assertEquals("Sheet2", tableSheet1.getTableName());
-		Assert.assertEquals(1, tableSheet1.getColumnCount());
-	}
-	
-	@Test
-	public void testInsertSheet() throws Exception{
-		File file = new File(ResourceUtilities.getAbsolutePath(TEST_FILE));
-		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);
-		//index <0 , Not expected
-		Table table = spDocument.insertSheet(-1);
-		Assert.assertNull(table);
-		//index >= sheet count
-		Table tableb = spDocument.insertSheet(11);
-		Assert.assertNull(tableb);
-		
-		//index is within the law
-		Table tab = spDocument.getSheetByName("tabellDemo2");
-		if(tab != null){
-			for(int i=0;i<spDocument.getSheetCount();i++){
-				if(tab.equals(spDocument.getSheetByIndex(i)))
-					spDocument.removeSheet(i);
-			}
-		}
-		Table tablea = spDocument.insertSheet(0);
-		Column col = tablea.appendColumn();
-		col.setWidth(12.99);
-		Column col2 = tablea.appendColumn();
-		col.setWidth(12.);
-		tablea.setTableName("tabellDemo2");
-		Assert.assertEquals("tabellDemo2", tablea.getTableName());
-		spDocument.save(ResourceUtilities.getAbsolutePath(TEST_FILE));
-		
-	}
-	
-	@Test
-	public void testRemoveSheet() throws Exception{
-		File file = new File(ResourceUtilities.getAbsolutePath(TEST_FILE));
-		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);
-		//index <0 , Not expected
-		Table table = spDocument.insertSheet(-1);
-		Assert.assertNull(table);
-		//index >= sheet count
-		Table tableb = spDocument.insertSheet(11);
-		Assert.assertNull(tableb);
-		
-		//index is within the law
-		Table tab = spDocument.getSheetByName("tabellDemo2");
-		if(tab != null){
-			for(int i=0;i<spDocument.getSheetCount();i++){
-				if(tab.equals(spDocument.getSheetByIndex(i)))
-					spDocument.removeSheet(i);
-			}
-		}
-		Table tablea = spDocument.insertSheet(0);
-		Column col = tablea.appendColumn();
-		col.setWidth(12.99);
-		Column col2 = tablea.appendColumn();
-		col.setWidth(12.);
-		tablea.setTableName("tabellDemo2");
-		Assert.assertEquals("tabellDemo2", tablea.getTableName());
-		spDocument.removeSheet(0);
-		Table tablem = spDocument.getSheetByIndex(0);
-		Assert.assertNotSame(tablea, tablem);
-		
-		spDocument.save(ResourceUtilities.getAbsolutePath(TEST_FILE));
-	}
-	
-	@Test
-	public void testGetSheetByIndex2() throws Exception{
-		File file = new File(ResourceUtilities.getAbsolutePath(TEST_FILE));
-		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);
-		//index <0 , Not expected
-		Table table = spDocument.insertSheet(-1);
-		Assert.assertNull(table);
-		//index >= sheet count
-		Table tableb = spDocument.insertSheet(11);
-		Assert.assertNull(tableb);
-		
-		//index is within the law
-		Table tab = spDocument.getSheetByName("tabellDemo1");
-		if(tab != null)
-			tab.remove();
-		
-		Table taba = spDocument.getSheetByName("Tabelle1");
-		Table tablea = spDocument.insertSheet(taba,0);
-		tablea.setTableName("tabellDemo1");
-		Assert.assertEquals("tabellDemo1", tablea.getTableName());
-		Table tablem = spDocument.getSheetByIndex(0);
-		Assert.assertEquals(tablea, tablem);
-		
-		spDocument.save(ResourceUtilities.getAbsolutePath(TEST_FILE));
-		
-	}
-	
-}
+/* 

+Licensed to the Apache Software Foundation (ASF) under one

+or more contributor license agreements.  See the NOTICE file

+distributed with this work for additional information

+regarding copyright ownership.  The ASF licenses this file

+to you under the Apache License, Version 2.0 (the

+"License"); you may not use this file except in compliance

+with the License.  You may obtain a copy of the License at

+

+ http://www.apache.org/licenses/LICENSE-2.0

+

+Unless required by applicable law or agreed to in writing,

+software distributed under the License is distributed on an

+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+KIND, either express or implied.  See the License for the

+specific language governing permissions and limitations

+under the License.

+*/

+

+package org.odftoolkit.simple;

+

+import java.awt.Rectangle;

+import java.io.File;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+import junit.framework.Assert;

+

+import org.junit.Test;

+import org.odftoolkit.simple.Document.OdfMediaType;

+import org.odftoolkit.simple.chart.Chart;

+import org.odftoolkit.simple.chart.ChartType;

+import org.odftoolkit.simple.chart.DataSet;

+import org.odftoolkit.simple.table.Column;

+import org.odftoolkit.simple.table.Table;

+import org.odftoolkit.simple.utils.ResourceUtilities;

+

+public class SpreadsheetDocumentTest {

+	private static final Logger LOG = Logger.getLogger(SpreadsheetDocumentTest.class.getName());

+	private static final String TEST_FILE = "spreadsheetTestTemplate.ots";

+	

+	@Test

+	public void testGetMediaTypeString() throws Exception{

+		

+		try {

+			String spreadDocPath = ResourceUtilities.getAbsolutePath(TEST_FILE);

+			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(spreadDocPath);

+			Assert.assertNotNull(spreadDoc);

+			OdfMediaType odfMediaType = spreadDoc.getOdfMediaType();

+			Assert.assertEquals(Document.OdfMediaType.SPREADSHEET_TEMPLATE, odfMediaType);

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+		

+	}

+	

+	@Test

+	public void testGetSuffix() throws Exception{

+		try {

+			String spreadDocPath = ResourceUtilities.getAbsolutePath(TEST_FILE);

+			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(spreadDocPath);

+			Assert.assertNotNull(spreadDoc);

+			OdfMediaType odfMediaType = spreadDoc.getOdfMediaType();

+			String suffix = odfMediaType.getSuffix();

+			Assert.assertEquals("ots", suffix);

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+	}

+	

+	@Test

+	public void testGetOdfMediaType() throws Exception{

+		

+		try {

+			String spreadDocPath = ResourceUtilities.getAbsolutePath(TEST_FILE);

+			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(spreadDocPath);

+			Assert.assertNotNull(spreadDoc);

+			Document.OdfMediaType odfMediaType = SpreadsheetDocument.OdfMediaType.getOdfMediaType(spreadDoc.getMediaTypeString());

+			Assert.assertEquals("ots", odfMediaType.getSuffix());

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+	}

+	

+	@Test

+	public void testLoadDocument() throws Exception{

+		try {

+			String filePath = ResourceUtilities.getAbsolutePath(TEST_FILE);

+			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(filePath);

+			Assert.assertNotNull(spreadDoc);

+			Assert.assertEquals(Document.OdfMediaType.SPREADSHEET_TEMPLATE, spreadDoc.getOdfMediaType());

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+	}

+	

+	@Test

+	public void testLoadDocumentFile() throws Exception{

+		

+		try {

+			String filePath = ResourceUtilities.getAbsolutePath(TEST_FILE);

+			File fileDoc = new File(filePath);

+			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(fileDoc);;

+			Assert.assertNotNull(spreadDoc);

+			Assert.assertEquals(Document.OdfMediaType.SPREADSHEET_TEMPLATE, spreadDoc.getOdfMediaType());

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+		

+	}

+

+	@Test

+	public void testLoadDocumentFileWithExcelDummyRows0() {

+		testLoadDocumentFileWithExcelDummyRows("ExcelDummyRowProblem_rows0.ods", 0);

+	}

+

+	@Test

+	public void testLoadDocumentFileWithExcelDummyRows1() {

+		testLoadDocumentFileWithExcelDummyRows("ExcelDummyRowProblem_rows1.ods", 1);

+	}

+

+	private void testLoadDocumentFileWithExcelDummyRows(String file, int rowCount) {

+		try {

+			String filePath = ResourceUtilities.getAbsolutePath(file);

+			File fileDoc = new File(filePath);

+			SpreadsheetDocument spreadDoc = SpreadsheetDocument.loadDocument(fileDoc);

+			Assert.assertNotNull(spreadDoc);

+			Table table = spreadDoc.getSheetByIndex(0);

+			Assert.assertNotNull(table);

+			Assert.assertEquals(rowCount, table.getRowCount());

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+	}

+	

+	@Test

+	public void testCreateChart() throws Exception{

+		try {

+			SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(ResourceUtilities.getAbsolutePath(TEST_FILE));

+			String title = "XXXTitle";

+			String[] lables = new String[]{"spring","summer","autumn","autumn"};

+			String[] legends = new String[]{"hello1","hello2","hello3"};

+			double[][] data = new double[][]{{1.2,2.22,3},{2,3,4},{3,4,5},{4,5,6}};

+			DataSet dataset = new DataSet(lables, legends, data);

+			Rectangle rect = new Rectangle();

+			rect.x = 367;

+			rect.y = 389;

+			rect.width = 379;

+			rect.height = 424;

+			Chart spChart = spDocument.createChart(title, dataset, rect);

+			Assert.assertNotNull(spChart);

+			spChart.setChartType(ChartType.AREA);

+			//save

+			spDocument.save(ResourceUtilities.getTestOutput("Chart_"+TEST_FILE));

+			

+			Assert.assertEquals(dataset, spChart.getChartData());

+			Assert.assertEquals("XXXTitle", spChart.getChartTitle());

+			Assert.assertEquals(ChartType.AREA, spChart.getChartType());

+			

+			LOG.log(Level.INFO,"spChart--> " + spChart);

+			

+		} catch (Exception e) {

+			LOG.log(Level.SEVERE, e.getMessage(), e);

+			Assert.fail(e.getMessage());

+		}

+	}

+	

+	@Test

+	public void testGetSheetByIndex() throws Exception{

+		File file = new File(ResourceUtilities.getAbsolutePath("TestSpreadsheetTable.ods"));

+		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);

+		//index < 0 , Not expected, table ==null

+		Table tablenull = spDocument.getSheetByIndex(-1);

+		Assert.assertTrue((tablenull == null));

+		//index > 0 

+		//index = 0

+		Table tableSheet0 = spDocument.getSheetByIndex(0);

+		Assert.assertTrue((tableSheet0 != null));

+		Assert.assertEquals("Sheet1", tableSheet0.getTableName());

+		Assert.assertEquals(29, tableSheet0.getColumnCount());

+		//index = 1

+		Table tableSheet1 = spDocument.getSheetByIndex(1);

+		Assert.assertTrue((tableSheet1 != null));

+		Assert.assertEquals("Sheet2", tableSheet1.getTableName());

+		Assert.assertEquals(1, tableSheet1.getColumnCount());

+	}

+	

+	@Test

+	public void testInsertSheet() throws Exception{

+		File file = new File(ResourceUtilities.getAbsolutePath(TEST_FILE));

+		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);

+		//index <0 , Not expected

+		Table table = spDocument.insertSheet(-1);

+		Assert.assertNull(table);

+		//index >= sheet count

+		Table tableb = spDocument.insertSheet(11);

+		Assert.assertNull(tableb);

+		

+		//index is within the law

+		Table tab = spDocument.getSheetByName("tabellDemo2");

+		if(tab != null){

+			for(int i=0;i<spDocument.getSheetCount();i++){

+				if(tab.equals(spDocument.getSheetByIndex(i)))

+					spDocument.removeSheet(i);

+			}

+		}

+		Table tablea = spDocument.insertSheet(0);

+		Column col = tablea.appendColumn();

+		col.setWidth(12.99);

+		Column col2 = tablea.appendColumn();

+		col.setWidth(12.);

+		tablea.setTableName("tabellDemo2");

+		Assert.assertEquals("tabellDemo2", tablea.getTableName());

+		spDocument.save(ResourceUtilities.getAbsolutePath(TEST_FILE));

+		

+	}

+	

+	@Test

+	public void testRemoveSheet() throws Exception{

+		File file = new File(ResourceUtilities.getAbsolutePath(TEST_FILE));

+		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);

+		//index <0 , Not expected

+		Table table = spDocument.insertSheet(-1);

+		Assert.assertNull(table);

+		//index >= sheet count

+		Table tableb = spDocument.insertSheet(11);

+		Assert.assertNull(tableb);

+		

+		//index is within the law

+		Table tab = spDocument.getSheetByName("tabellDemo2");

+		if(tab != null){

+			for(int i=0;i<spDocument.getSheetCount();i++){

+				if(tab.equals(spDocument.getSheetByIndex(i)))

+					spDocument.removeSheet(i);

+			}

+		}

+		Table tablea = spDocument.insertSheet(0);

+		Column col = tablea.appendColumn();

+		col.setWidth(12.99);

+		Column col2 = tablea.appendColumn();

+		col.setWidth(12.);

+		tablea.setTableName("tabellDemo2");

+		Assert.assertEquals("tabellDemo2", tablea.getTableName());

+		spDocument.removeSheet(0);

+		Table tablem = spDocument.getSheetByIndex(0);

+		Assert.assertNotSame(tablea, tablem);

+		

+		spDocument.save(ResourceUtilities.getAbsolutePath(TEST_FILE));

+	}

+	

+	@Test

+	public void testGetSheetByIndex2() throws Exception{

+		File file = new File(ResourceUtilities.getAbsolutePath(TEST_FILE));

+		SpreadsheetDocument spDocument = SpreadsheetDocument.loadDocument(file);

+		//index <0 , Not expected

+		Table table = spDocument.insertSheet(-1);

+		Assert.assertNull(table);

+		//index >= sheet count

+		Table tableb = spDocument.insertSheet(11);

+		Assert.assertNull(tableb);

+		

+		//index is within the law

+		Table tab = spDocument.getSheetByName("tabellDemo1");

+		if(tab != null)

+			tab.remove();

+		

+		Table taba = spDocument.getSheetByName("Tabelle1");

+		Table tablea = spDocument.insertSheet(taba,0);

+		tablea.setTableName("tabellDemo1");

+		Assert.assertEquals("tabellDemo1", tablea.getTableName());

+		Table tablem = spDocument.getSheetByIndex(0);

+		Assert.assertEquals(tablea, tablem);

+		

+		spDocument.save(ResourceUtilities.getAbsolutePath(TEST_FILE));

+		

+	}

+	

+}

diff --git a/simple/src/test/resources/ExcelDummyRowProblem_rows0.ods b/simple/src/test/resources/ExcelDummyRowProblem_rows0.ods
new file mode 100644
index 0000000..b3add5c
--- /dev/null
+++ b/simple/src/test/resources/ExcelDummyRowProblem_rows0.ods
Binary files differ
diff --git a/simple/src/test/resources/ExcelDummyRowProblem_rows1.ods b/simple/src/test/resources/ExcelDummyRowProblem_rows1.ods
new file mode 100644
index 0000000..8c27b32
--- /dev/null
+++ b/simple/src/test/resources/ExcelDummyRowProblem_rows1.ods
Binary files differ