Package org.odftoolkit.simple.table

Source Code of org.odftoolkit.simple.table.Cell

/*
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 java.awt.FontMetrics;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.net.URI;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.JTextField;

import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.attribute.fo.FoTextAlignAttribute;
import org.odftoolkit.odfdom.dom.attribute.office.OfficeValueTypeAttribute;
import org.odftoolkit.odfdom.dom.element.OdfStylableElement;
import org.odftoolkit.odfdom.dom.element.OdfStyleBase;
import org.odftoolkit.odfdom.dom.element.dc.DcCreatorElement;
import org.odftoolkit.odfdom.dom.element.dc.DcDateElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawImageElement;
import org.odftoolkit.odfdom.dom.element.number.NumberCurrencySymbolElement;
import org.odftoolkit.odfdom.dom.element.number.NumberNumberElement;
import org.odftoolkit.odfdom.dom.element.number.NumberTextElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationElement;
import org.odftoolkit.odfdom.dom.element.style.StyleTableCellPropertiesElement;
import org.odftoolkit.odfdom.dom.element.table.TableContentValidationElement;
import org.odftoolkit.odfdom.dom.element.table.TableContentValidationsElement;
import org.odftoolkit.odfdom.dom.element.table.TableCoveredTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableHelpMessageElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableCellElementBase;
import org.odftoolkit.odfdom.dom.element.table.TableTableColumnElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableHeaderRowsElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowGroupElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowsElement;
import org.odftoolkit.odfdom.dom.element.text.TextHElement;
import org.odftoolkit.odfdom.dom.element.text.TextListElement;
import org.odftoolkit.odfdom.dom.element.text.TextPElement;
import org.odftoolkit.odfdom.dom.style.props.OdfStylePropertiesSet;
import org.odftoolkit.odfdom.dom.style.props.OdfStyleProperty;
import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberCurrencyStyle;
import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberDateStyle;
import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberPercentageStyle;
import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberStyle;
import org.odftoolkit.odfdom.incubator.doc.number.OdfNumberTimeStyle;
import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeAutomaticStyles;
import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;
import org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph;
import org.odftoolkit.odfdom.incubator.doc.text.OdfTextSpan;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.odftoolkit.odfdom.pkg.OdfName;
import org.odftoolkit.odfdom.pkg.OdfPackage;
import org.odftoolkit.odfdom.pkg.OdfXMLFactory;
import org.odftoolkit.odfdom.type.Color;
import org.odftoolkit.odfdom.type.Length;
import org.odftoolkit.odfdom.type.Length.Unit;
import org.odftoolkit.simple.Component;
import org.odftoolkit.simple.Document;
import org.odftoolkit.simple.SpreadsheetDocument;
import org.odftoolkit.simple.common.TextExtractor;
import org.odftoolkit.simple.common.WhitespaceProcessor;
import org.odftoolkit.simple.draw.FrameContainer;
import org.odftoolkit.simple.draw.FrameRectangle;
import org.odftoolkit.simple.draw.Image;
import org.odftoolkit.simple.style.Border;
import org.odftoolkit.simple.style.Font;
import org.odftoolkit.simple.style.StyleTypeDefinitions;
import org.odftoolkit.simple.style.StyleTypeDefinitions.CellBordersType;
import org.odftoolkit.simple.style.StyleTypeDefinitions.FontStyle;
import org.odftoolkit.simple.style.StyleTypeDefinitions.HorizontalAlignmentType;
import org.odftoolkit.simple.style.StyleTypeDefinitions.VerticalAlignmentType;
import org.odftoolkit.simple.text.AbstractParagraphContainer;
import org.odftoolkit.simple.text.Paragraph;
import org.odftoolkit.simple.text.ParagraphContainer;
import org.odftoolkit.simple.text.list.AbstractListContainer;
import org.odftoolkit.simple.text.list.ListContainer;
import org.odftoolkit.simple.text.list.ListDecorator;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Cell represents table cell feature in ODF document.
* <p>
* Table provides methods to get/set/modify the cell content and cell
* properties.
*/
public class Cell extends Component implements ListContainer, ParagraphContainer, FrameContainer {

  TableTableCellElementBase mCellElement;
  Document mDocument;

  int mnRepeatedColIndex;
  int mnRepeatedRowIndex;
  Table mOwnerTable;
  String msFormatString;
  CellStyleHandler mStyleHandler;

  /**
   * The default date format of table cell.
   */
  private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
  /**
   * The default time format of table cell.
   */
  private static final String DEFAULT_TIME_FORMAT = "'PT'HH'H'mm'M'ss'S'";
  // example format: 2002-05-30T09:30:10
  private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
  /**
   * The default cell back color of table cell.
   */
  private static final String DEFAULT_BACKGROUND_COLOR = "#FFFFFF";
  /**
   * The default column spanned number.
   */
  private static final int DEFAULT_COLUMN_SPANNED_NUMBER = 1;
  /**
   * The default row spanned number.
   */
  private static final int DEFAULT_ROW_SPANNED_NUMBER = 1;
  /**
   * The default columns repeated number.
   */
  private static final int DEFAULT_COLUMNS_REPEATED_NUMBER = 1;
 
  private ParagraphContainerImpl paragraphContainerImpl;
  private ListContainerImpl listContainerImpl;

  Cell(TableTableCellElementBase odfElement, int repeatedColIndex, int repeatedRowIndex) {
    mCellElement = odfElement;
    mnRepeatedColIndex = repeatedColIndex;
    mnRepeatedRowIndex = repeatedRowIndex;
    mOwnerTable = getTable();
    mDocument = ((Document) ((OdfFileDom) mCellElement.getOwnerDocument()).getDocument());
    mStyleHandler = new CellStyleHandler(this);
  }

  /**
   * Get the <code>Cell</code> instance from the
   * <code>TableTableCellElementBase</code> instance.
   * <p>
   * Each <code>TableTableCellElementBase</code> instance has a one-to-one
   * relationship to the a <code>Cell</code> instance.
   *
   * @param cellElement
   *            the cell element that need to get the corresponding
   *            <code>Cell</code> instance
   * @return the <code>Cell</code> instance that represents a specified cell
   *         element
   */
  public static Cell getInstance(TableTableCellElementBase cellElement) {
    TableTableElement tableElement = null;
    Node node = cellElement.getParentNode();
    while (node != null) {
      if (node instanceof TableTableElement) {
        tableElement = (TableTableElement) node;
      }
      node = node.getParentNode();
    }
    Table table = null;
    if (tableElement != null) {
      table = Table.getInstance(tableElement);
    } else {
      throw new IllegalArgumentException("the cellElement is not in the table dom tree");
    }

    Cell cell = table.getCellInstance(cellElement, 0, 0);
    int colRepeatedNum = cell.getColumnsRepeatedNumber();
    int rowRepeatedNum = cell.getTableRow().getRowsRepeatedNumber();
    if (colRepeatedNum > 1 && rowRepeatedNum > 1) {
      if (colRepeatedNum > 1) {
        Logger
            .getLogger(Cell.class.getName())
            .log(Level.WARNING,
                "the cell has the repeated column number, and puzzled about get which repeated column index of the cell,");
      }
      if (rowRepeatedNum > 1) {
        Logger
            .getLogger(Cell.class.getName())
            .log(
                Level.WARNING,
                "the row contains the current cell has the repeated row number, and puzzled about get which repeated row index of the cell,");
      }
      Logger
          .getLogger(Cell.class.getName())
          .log(Level.WARNING,
              "here just return the first cell that the repeated column index is 0 and repeated row index is 0, too.");
    }
    return cell;
  }

  /**
   * Return the horizontal alignment setting of this cell.
   * <p>
   * The returned value can be "center", "end", "justify", "left", "right", or
   * "start". If no horizontal alignment is set, null will be returned.
   *
   * @return the horizontal alignment setting.
   *
   * @see #getHorizontalAlignmentType()
   * @deprecated As of Simple version 0.3, replaced by
   *             <code>getHorizontalAlignmentType()</code>
   */
  @Deprecated
  public String getHorizontalAlignment() {
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForRead();
    if (styleElement != null) {
      OdfStyleProperty property = OdfStyleProperty.get(OdfStylePropertiesSet.ParagraphProperties, OdfName
          .newName(OdfDocumentNamespace.FO, "text-align"));
      return styleElement.getProperty(property);
    }
    return null;
  }

  /**
   * Return the horizontal alignment setting of this cell.
   * <p>
   * Null will returned if there is no explicit style definition for this
   * cell.
   * <p>
   * Default value will be returned if explicit style definition is found but
   * no horizontal alignment is set.
   *
   * @return the horizontal alignment setting.
   */
  public HorizontalAlignmentType getHorizontalAlignmentType() {
    return getStyleHandler().getHorizontalAlignment();
  }

  /**
   * Set the horizontal alignment setting of this cell.
   * <p>
   * The parameter can be "center", "end", "justify", "left", "right", or
   * "start". Actually, "left" will be interpreted as "start", while "right"
   * will be interpreted as "end". If argument is null, the explicit
   * horizontal alignment setting is removed.
   *
   * @param horizontalAlignment
   *            the horizontal alignment setting.
   * @see #setHorizontalAlignment(StyleTypeDefinitions.HorizontalAlignmentType)
   * @deprecated As of Simple version 0.3, replaced by
   *             <code>setHorizontalAlignment(SimpleHorizontalAlignmentType)</code>
   */
  @Deprecated
  public void setHorizontalAlignment(String horizontalAlignment) {
    if (FoTextAlignAttribute.Value.LEFT.toString().equalsIgnoreCase(horizontalAlignment)) {
      horizontalAlignment = FoTextAlignAttribute.Value.START.toString();
    }
    if (FoTextAlignAttribute.Value.RIGHT.toString().equalsIgnoreCase(horizontalAlignment)) {
      horizontalAlignment = FoTextAlignAttribute.Value.END.toString();
    }
    splitRepeatedCells();
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForWrite();
    if (styleElement != null) {
      OdfStyleProperty property = OdfStyleProperty.get(OdfStylePropertiesSet.ParagraphProperties, OdfName
          .newName(OdfDocumentNamespace.FO, "text-align"));
      if (horizontalAlignment != null) {
        styleElement.setProperty(property, horizontalAlignment);
      } else {
        styleElement.removeProperty(property);
      }
    }
  }

  /**
   * Set the horizontal alignment setting of this cell. If the alignment is
   * set as Default, the explicit horizontal alignment setting is removed.
   *
   * @param alignType
   *            the horizontal alignment setting.
   */
  public void setHorizontalAlignment(HorizontalAlignmentType alignType) {
    getStyleHandler().setHorizontalAlignment(alignType);
  }

  /**
   * Return the vertical alignment setting of this cell.
   * <p>
   * The returned value can be "auto", "automatic", "baseline", "bottom",
   * "middle", or "top".
   *
   * @return the vertical alignment setting of this cell.
   *
   * @see #getVerticalAlignmentType()
   * @deprecated As of Simple version 0.3, replaced by
   *             <code>getVerticalAlignmentType()</code>
   */
  @Deprecated
  public String getVerticalAlignment() {
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForRead();
    if (styleElement != null) {
      OdfStyleProperty property = OdfStyleProperty.get(OdfStylePropertiesSet.TableCellProperties, OdfName
          .newName(OdfDocumentNamespace.STYLE, "vertical-align"));
      return styleElement.getProperty(property);
    }
    return null;
  }

  /**
   * Return the vertical alignment setting of this cell.
   * <p>
   * Null will returned if there is no explicit style definition for this
   * cell.
   * <p>
   * Default value will be returned if explicit style definition is found but
   * no vertical alignment is set.
   *
   * @return the vertical alignment setting.
   */
  public VerticalAlignmentType getVerticalAlignmentType() {
    return getStyleHandler().getVerticalAlignment();
  }

  /**
   * Set the vertical alignment setting of this cell.
   * <p>
   * The parameter can be "auto", "automatic", "baseline", "bottom", "middle",
   * or "top". If argument is null, the explicit vertical alignment setting is
   * removed.
   *
   * @param verticalAlignment
   *            the vertical alignment setting.
   * @see #setVerticalAlignment(StyleTypeDefinitions.VerticalAlignmentType)
   * @deprecated As of Simple version 0.3, replaced by
   *             <code>setVerticalAlignment(SimpleVerticalAlignmentType)</code>
   */
  @Deprecated
  public void setVerticalAlignment(String verticalAlignment) {
    splitRepeatedCells();
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForWrite();
    if (styleElement != null) {
      OdfStyleProperty property = OdfStyleProperty.get(OdfStylePropertiesSet.TableCellProperties, OdfName
          .newName(OdfDocumentNamespace.STYLE, "vertical-align"));
      if (verticalAlignment != null) {
        styleElement.setProperty(property, verticalAlignment);
      } else {
        styleElement.removeProperty(property);
      }
    }
  }

  /**
   * Set the vertical alignment setting of this cell.
   * <p>
   * If the alignment is set as Default or null, the explicit vertical
   * alignment setting is removed.
   *
   * @param verticalAlignment
   *            the vertical alignment setting.
   */
  public void setVerticalAlignment(VerticalAlignmentType verticalAlignment) {
    getStyleHandler().setVerticalAlignment(verticalAlignment);
  }

  /**
   * Return the wrap option of this cell.
   *
   * @return true if the cell content can be wrapped;
   *         <p>
   *         false if the cell content cannot be wrapped.
   */
  public boolean isTextWrapped() {
    return getStyleHandler().isTextWrapped();
  }

  /**
   * Set the wrap option of this cell.
   *
   * @param isTextWrapped
   *            whether the cell content can be wrapped or not
   */
  public void setTextWrapped(boolean isTextWrapped) {
    getStyleHandler().setTextWrapped(isTextWrapped);
  }

  private TableTableRowElement findRowInTableHeaderRows(TableTableHeaderRowsElement headers, TableTableRowElement tr,
      int[] indexs) {
    int result = 0;
    for (Node m : new DomNodeList(headers.getChildNodes())) {
      if (m == tr) {
        indexs[0] = result;
        return tr;
      }
      if (m instanceof TableTableRowElement) {
        result += ((TableTableRowElement) m).getTableNumberRowsRepeatedAttribute().intValue();
      }
    }
    indexs[0] = result;
    return null;
  }

  private TableTableRowElement findRowInTableRows(TableTableRowsElement rows, TableTableRowElement tr, int[] indexs) {
    int result = 0;
    for (Node m : new DomNodeList(rows.getChildNodes())) {
      if (m == tr) {
        indexs[0] = result;
        return tr;
      }
      if (m instanceof TableTableRowElement) {
        result += ((TableTableRowElement) m).getTableNumberRowsRepeatedAttribute().intValue();
      }
    }
    indexs[0] = result;
    return null;
  }

  private TableTableRowElement findRowInTableRowGroup(OdfElement group, TableTableRowElement tr, int[] indexs) {
    int result = 0;
    int[] resultIndexs = new int[1];

    if (!(group instanceof TableTableRowGroupElement) && !(group instanceof TableTableElement)) {
      indexs[0] = 0;
      return null;
    }

    for (Node m : new DomNodeList(group.getChildNodes())) {
      if (m instanceof TableTableHeaderRowsElement) {
        TableTableHeaderRowsElement headers = (TableTableHeaderRowsElement) m;
        TableTableRowElement returnEle = findRowInTableHeaderRows(headers, tr, resultIndexs);
        result += resultIndexs[0];
        if (returnEle != null) {// find
          indexs[0] = result;
          return returnEle;
        }
      } else if (m instanceof TableTableRowGroupElement) {
        TableTableRowGroupElement aGroup = (TableTableRowGroupElement) m;
        TableTableRowElement returnEle = findRowInTableRowGroup(aGroup, tr, resultIndexs);
        result += resultIndexs[0];
        if (returnEle != null) {// find
          indexs[0] = result;
          return returnEle;
        }
      } else if (m instanceof TableTableRowsElement) {
        TableTableRowsElement rows = (TableTableRowsElement) m;
        TableTableRowElement returnEle = findRowInTableRows(rows, tr, resultIndexs);
        result += resultIndexs[0];
        if (returnEle != null) {// find
          indexs[0] = result;
          return returnEle;
        }
      } else if (m instanceof TableTableRowElement) {
        if (m == tr) { // find
          indexs[0] = result;
          return tr;
        }
        result += ((TableTableRowElement) m).getTableNumberRowsRepeatedAttribute().intValue();
      }
    }
    indexs[0] = result;
    return null;
  }

  /**
   * Get the index of the table row which contains this cell.
   *
   * @return the index of the row containing this cell
   */
  public int getRowIndex() {
    TableTableElement table = getTableElement();
    TableTableRowElement tr = getTableRowElement();
    int[] indexs = new int[1];

    TableTableRowElement returnEle = findRowInTableRowGroup(table, tr, indexs);
    if (returnEle != null) {
      return (indexs[0] + mnRepeatedRowIndex);
    } else {
      return -1;
    }
  }

  /**
   * Get an instance of table feature which contains this cell.
   *
   * @return the table containing this cell
   */
  public Table getTable() {
    TableTableElement tableElement = getTableElement();
    if (tableElement != null) {
      return Table.getInstance(tableElement);
    }
    return null;
  }

  /**
   * Get the index of the table column which contains this cell.
   *
   * @return the index of the column containing this cell
   */
  public int getColumnIndex() {
    TableTableRowElement tr = (TableTableRowElement) mCellElement.getParentNode();
    int result = 0;
    for (Node n : new DomNodeList(tr.getChildNodes())) {
      if (n == mCellElement) {
        return result + mnRepeatedColIndex;
      }
      if (n instanceof TableTableCellElementBase) {
        result += ((TableTableCellElementBase) n).getTableNumberColumnsRepeatedAttribute().intValue();
      }
    }
    return result;
  }

  /**
   * Get the instance of table column feature which contains this cell.
   *
   * @return the instance of table column feature which contains the cell.
   */
  public Column getTableColumn() {
    Table table = getTable();
    int index = getColumnIndex();
    return table.getColumnByIndex(index);
  }

  TableTableColumnElement getTableColumnElement() {
    // return OdfTableCellBaseImpl.getTableColumn((OdfTableCellBase)
    // mCellElement);
    TableTableElement tableElement = getTableElement();
    int columnindex = getColumnIndex();
    Table fTable = Table.getInstance(tableElement);
    return fTable.getColumnElementByIndex(columnindex);
  }

  /**
   * Get the instance of table row feature which contains this cell.
   *
   * @return the instance of table row feature which contains the cell.
   */
  public Row getTableRow() {
    Table table = getTable();
    return table.getRowInstance(getTableRowElement(), mnRepeatedRowIndex);
  }

  private TableTableRowElement getTableRowElement() {
    Node node = mCellElement.getParentNode();
    if (node instanceof TableTableRowElement) {
      return (TableTableRowElement) node;
    }
    return null;
  }

  /**
   * Get the table object who contains this cell.
   *
   * @return the table object who contains the cell.
   */
  private TableTableElement getTableElement() {
    Node node = mCellElement.getParentNode();
    while (node != null) {
      if (node instanceof TableTableElement) {
        return (TableTableElement) node;
      }
      node = node.getParentNode();
    }
    return null;
  }

  /**
   * Get the cell that covers this cell.
   * <p>
   * If the cell is a covered cell, the owner cell will be returned; if the
   * cell is a real cell , the cell itself will be returned.
   *
   * @return the cell that covers the current cell
   */
  public Cell getOwnerTableCell() {
    Table ownerTable = getTable();
    List<CellCoverInfo> coverList = ownerTable.getCellCoverInfos(0, 0, ownerTable.getColumnCount() - 1, ownerTable
        .getRowCount() - 1);
    return ownerTable.getOwnerCellByPosition(coverList, getColumnIndex(), getRowIndex());
  }

  /**
   * Get the instance of <code>TableTableCellElementBase</code> which
   * represents this cell.
   *
   * @return the instance of <code>TableTableCellElementBase</code>
   */
  public TableTableCellElementBase getOdfElement() {
    return mCellElement;
  }

  /**
   * Return the currency code of this cell, for example, "USD", "EUR", "CNY",
   * and etc.
   * <p>
   * If the value type is not "currency", an IllegalArgumentException will be
   * thrown.
   *
   * @return the currency code
   *         <p>
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown if the value type
   *             is not "currency".
   */
  public String getCurrencyCode() {
    if (mCellElement.getOfficeValueTypeAttribute().equals(OfficeValueTypeAttribute.Value.CURRENCY.toString())) {
      return mCellElement.getOfficeCurrencyAttribute();
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Set the currency code of this cell, for example, "USD", "EUR", "CNY", and
   * etc.
   *
   * @param currency
   *            the currency code that need to be set.
   * @throws IllegalArgumentException
   *             If input <code>currency</code> is null, an
   *             IllegalArgumentException will be thrown.
   */
  public void setCurrencyCode(String currency) {
    if (currency == null) {
      throw new IllegalArgumentException("Currency code of cell should not be null.");
    }
    splitRepeatedCells();
    if (mCellElement.getOfficeValueTypeAttribute().equals(OfficeValueTypeAttribute.Value.CURRENCY.toString())) {
      mCellElement.setOfficeCurrencyAttribute(currency);
    } else {
      throw new IllegalArgumentException();
    }
  }

  private void setTypeAttr(OfficeValueTypeAttribute.Value type) {
    mCellElement.setOfficeValueTypeAttribute(type.toString());
  }

  /**
   * Set the value type of this cell. The parameter can be "boolean",
   * "currency", "date", "float", "percentage", "string" or "time".
   * <p>
   * If the parameter <code>type</code> is not a valid cell type, an
   * IllegalArgumentException will be thrown.
   *
   * @param type
   *            the type that need to be set If input type is null, an
   *            IllegalArgumentException will be thrown.
   */
  public void setValueType(String type) {
    if (type == null) {
      throw new IllegalArgumentException("type shouldn't be null.");
    }
    String sType = type.toLowerCase();
    OfficeValueTypeAttribute.Value value = OfficeValueTypeAttribute.Value.enumValueOf(sType);
    if (value == null) {
      throw new IllegalArgumentException("the value type of cell is not valid");
    }

    mCellElement.setOfficeValueTypeAttribute(sType);
  }

  /**
   * Get the value type of this cell. The returned value can be "boolean",
   * "currency", "date", "float", "percentage", "string" or "time". If no
   * value type is set, null will be returned.
   *
   * @return the type of the cell
   */
  public String getValueType() {
    return mCellElement.getOfficeValueTypeAttribute();
  }

  private OfficeValueTypeAttribute.Value getTypeAttr() {
    String type = mCellElement.getOfficeValueTypeAttribute();
    return OfficeValueTypeAttribute.Value.enumValueOf(type);
  }

  /**
   * Get the double value of this cell as Double object.
   * <p>
   * Throw IllegalArgumentException if the cell type is not "float".
   *
   * @return the double value of this cell as a Double object. If the cell
   *         value is empty, null will be returned.
   *         <p>
   *         An IllegalArgumentException will be thrown if the cell type is
   *         not "float".
   */
  public Double getDoubleValue() {
    if (getTypeAttr() == OfficeValueTypeAttribute.Value.FLOAT) {
      return mCellElement.getOfficeValueAttribute();
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Get the currency value of this cell as Double object.
   * <p>
   * Throw IllegalArgumentException if the cell type is not "currency".
   *
   * @return the currency value of this cell as a Double object. If the cell
   *         value is empty, null will be returned.
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown if the cell type
   *             is not "currency".
   */
  public Double getCurrencyValue() {
    if (getTypeAttr() == OfficeValueTypeAttribute.Value.CURRENCY) {
      return mCellElement.getOfficeValueAttribute();
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Get the symbol of currency.
   *
   * @return the currency symbol
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown if the value type
   *             is not "currency".
   */
  public String getCurrencySymbol() {
    if (getTypeAttr() != OfficeValueTypeAttribute.Value.CURRENCY) {
      throw new IllegalArgumentException();
    }

    OdfStyleBase style = getStyleHandler().getStyleElementForRead();
    if (style != null) {
      String dataStyleName = style.getOdfAttributeValue(OdfName.newName(OdfDocumentNamespace.STYLE,
          "data-style-name"));
      OdfNumberCurrencyStyle dataStyle = mCellElement.getAutomaticStyles().getCurrencyStyle(dataStyleName);
      if (dataStyle == null) {
        dataStyle = mDocument.getDocumentStyles().getCurrencyStyle(dataStyleName);
      }
      if ((dataStyle != null) && (dataStyle.getCurrencySymbolElement() != null)) {
        return dataStyle.getCurrencySymbolElement().getTextContent();
      }
    }
    return null;
  }

  /**
   * Set the value and currency of the cell, and set the value type as
   * "currency". If
   * <code>value</value> is null, the cell value will be removed.
   *
   * @param value
   *            the value that will be set
   * @param currency
   *            the currency that will be set.
   * @throws IllegalArgumentException
   *             If input currency is null, an IllegalArgumentException will
   *             be thrown.
   */
  public void setCurrencyValue(Double value, String currency) {
    if (currency == null) {
      throw new IllegalArgumentException("currency shouldn't be null.");
    }
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.CURRENCY);
    mCellElement.setOfficeValueAttribute(value);
    mCellElement.setOfficeCurrencyAttribute(currency);
  }

  /**
   * Get the cell percentage value as Double object.
   * <p>
   * Throw IllegalArgumentException if the cell type is not "percentage".
   *
   * @return the percentage value of this cell as a Double object. If the cell
   *         value is empty, null will be returned.
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown if the cell type
   *             is not "percentage".
   */
  public Double getPercentageValue() {
    if (getTypeAttr() == OfficeValueTypeAttribute.Value.PERCENTAGE) {
      return mCellElement.getOfficeValueAttribute();
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Set the cell value as a percentage value and set the value type as
   * percentage too. If
   * <code>value</value> is null, the cell value will be removed.
   *
   * @param value
   *            the value that will be set
   */
  public void setPercentageValue(Double value) {
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.PERCENTAGE);
    mCellElement.setOfficeValueAttribute(value);
  }

  /**
   * Get the text displayed in this cell.
   *
   * @return the text displayed in this cell
   */
  public String getDisplayText() {
    // TODO: This function doesn't work well if a cell contains a list.
    // Refer to testGetSetTextValue();
    return TextExtractor.getText(mCellElement);
  }

  /**
   * Set the text displayed in this cell. If content is null, it will display
   * the empty string instead.
   * <p>
   * Please note the displayed text in ODF viewer might be different with the
   * value set by this method, because the displayed text in ODF viewer is
   * calculated and set by editor. So an adapter can be assigned to adapt cell
   * value and value type.
   *
   * @param content
   *            the displayed text.
   * @param adapter
   *            the <code>CellValueAdapter</code> used to adapt cell value and
   *            value type.
   *
   * @see org.odftoolkit.simple.table.CellValueAdapter
   * @since 0.3
   */
  public void setDisplayText(String content, CellValueAdapter adapter) {
    if (content == null) {
      content = "";
    }
    setDisplayTextContent(content, null);
    // adapt value and value type by display text.
    adapter.adaptValue(this, content);
  }

  /**
   * Set the text displayed in this cell. If content is null, it will display
   * the empty string instead.
   * <p>
   * Please note the displayed text in ODF viewer might be different with the
   * value set by this method, because the displayed text in ODF viewer is
   * calculated and set by editor. The cell value and value type will be
   * updated follow by the rules which are designed in the
   * {@link org.odftoolkit.simple.table.DefaultCellValueAdapter
   * <code>DefaultCellValueAdapter</code>}.
   *
   * @param content
   *            the displayed text.
   *
   * @see org.odftoolkit.simple.table.CellValueAdapter
   * @see org.odftoolkit.simple.table.DefaultCellValueAdapter
   */
  public void setDisplayText(String content) {
    setDisplayText(content, CellValueAdapter.DEFAULT_VALUE_ADAPTER);
  }

  /**
   * Set the text displayed in this cell, with a specified style name.
   * <p>
   * Please note the displayed text in ODF viewer might be different with the
   * value set by this method, because the displayed text in ODF viewer is
   * calculated and set by editor. So an adapter can be assigned to adapt cell
   * value and value type.
   *
   * @param content
   *            the displayed text. If content is null, it will display the
   *            empty string instead.
   * @param adapter
   *            the <code>CellValueAdapter</code> used to adapt cell value and
   *            value type.
   * @param stylename
   *            the style name. If style name is null, the content will use
   *            the default paragraph style.
   *
   * @see org.odftoolkit.simple.table.CellValueAdapter
   * @since 0.3
   */
  public void setDisplayText(String content, CellValueAdapter adapter, String stylename) {
    if (content == null) {
      content = "";
    }
    setDisplayTextContent(content, stylename);
    // adapt value and value type by display text.
    adapter.adaptValue(this, content);
  }

  /**
   * Set the text displayed in this cell, with a specified style name.
   * <p>
   * Please note the displayed text in ODF viewer might be different with the
   * value set by this method, because the displayed text in ODF viewer is
   * calculated and set by editor. The cell value and value type will be
   * updated follow by the rules which are designed in the
   * {@link org.odftoolkit.simple.table.DefaultCellValueAdapter
   * <code>DefaultCellValueAdapter</code>}.
   *
   * @param content
   *            the displayed text. If content is null, it will display the
   *            empty string instead.
   * @param stylename
   *            the style name. If style name is null, the content will use
   *            the default paragraph style.
   *
   * @see org.odftoolkit.simple.table.CellValueAdapter
   * @see org.odftoolkit.simple.table.DefaultCellValueAdapter
   */
  public void setDisplayText(String content, String stylename) {
    setDisplayText(content, CellValueAdapter.DEFAULT_VALUE_ADAPTER, stylename);
  }

  // Set the text content in this cell. If content is null, it will display
  // the empty string instead.
  private void setDisplayTextContent(String content, String stylename) {
    WhitespaceProcessor textProcessor = new WhitespaceProcessor();
    OdfStylableElement element = OdfElement.findFirstChildNode(OdfTextParagraph.class, mCellElement);
    if (element == null) {
      removeContent();
      element = new OdfTextParagraph((OdfFileDom) mCellElement.getOwnerDocument());
      mCellElement.appendChild(element);
    } else {
      String formerContent = element.getTextContent();
      while (formerContent == null || "".equals(formerContent)) {
        OdfTextSpan span = OdfElement.findFirstChildNode(OdfTextSpan.class, element);
        if (span == null) {
          break;
        }
        formerContent = span.getTextContent();
        element = span;
      }
    }
    if ((stylename != null) && (stylename.length() > 0)) {
      element.setStyleName(stylename);
    }
    element.setTextContent(null);
    textProcessor.append(element, content);
    optimizeCellSize(content);
  }

  /**
   * Set the cell value as a double and set the value type to be "float".
   *
   * @param value
   *            the double value that will be set. If
   *            <code>value</value> is null, the cell value will be removed.
   */
  public void setDoubleValue(Double value) {
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.FLOAT);
    mCellElement.setOfficeValueAttribute(value);
    setDisplayTextContent(value + "", null);
  }

  /**
   * Get the cell boolean value as Boolean object.
   * <p>
   * Throw IllegalArgumentException if the cell type is not "boolean".
   *
   * @return the Boolean value of cell. If the cell value is empty, null will
   *         be returned.
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown if the cell type
   *             is not "boolean".
   */
  public Boolean getBooleanValue() {
    if (getTypeAttr() == OfficeValueTypeAttribute.Value.BOOLEAN) {
      return mCellElement.getOfficeBooleanValueAttribute();
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Set the cell value as a boolean and set the value type to be boolean. If
   * <code>value</value> is null, the cell value will be removed.
   *
   * @param value
   *            the value of boolean type
   */
  public void setBooleanValue(Boolean value) {
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.BOOLEAN);
    mCellElement.setOfficeBooleanValueAttribute(value);
    setDisplayTextContent(value + "", null);
  }

  /**
   * Get the cell date value as Calendar.
   * <p>
   * Throw IllegalArgumentException if the cell type is not "date".
   *
   * @return the Calendar value of cell
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown, if the cell type
   *             is not "date".
   */
  public Calendar getDateValue() {
    return getOfficeDateValue(DEFAULT_DATE_FORMAT);
  }
 
  /**
   * Get the cell date time value (date and time) as Calendar.
   * <p>
   * Throw IllegalArgumentException if the cell type is not "date".
   *
   * @return the Calendar value of cell
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown, if the cell type
   *             is not "date".
   */
  public Calendar getDateTimeValue() {
    return getOfficeDateValue(DEFAULT_DATE_TIME_FORMAT);
  }
 
  private Calendar getOfficeDateValue(String pattern) {
    if (getTypeAttr() == OfficeValueTypeAttribute.Value.DATE) {
      String dateStr = mCellElement.getOfficeDateValueAttribute();
      if (dateStr == null) {
        return null;
      }
      Date date = parseString(dateStr, pattern);
      Calendar calender = Calendar.getInstance();
      calender.setTime(date);
      return calender;
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Set the cell value as a date, and set the value type to be "date".
   *
   * @param date
   *            the value of {@link java.util.Calendar java.util.Calendar}
   *            type.
   */
  public void setDateValue(Calendar date) {
    setOfficeDateValue(date, DEFAULT_DATE_FORMAT);
  }
 
  /**
   * Sets the cell value as a date with second precision and the value type to be "date".
   * @param date
   */
  public void setDateTimeValue(Calendar date) {
    setOfficeDateValue(date, DEFAULT_DATE_TIME_FORMAT);
  }

  private void setOfficeDateValue(Calendar date, String pattern) {
    if (date == null) {
      throw new IllegalArgumentException("date shouldn't be null.");
    }
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.DATE);
    SimpleDateFormat simpleFormat = new SimpleDateFormat(pattern);
    String svalue = simpleFormat.format(date.getTime());
    mCellElement.setOfficeDateValueAttribute(svalue);
    setDisplayTextContent(svalue, null);
  }
 
  /**
   * Set the cell style name. When lots of cells have the same style features,
   * the user can configuration the first one and set the other's style name
   * directly. That will improve the performance.
   *
   * @param styleName
   *            an exit cell style name.
   * @since 0.4
   */
  public void setCellStyleName(String styleName) {
    mCellElement.setStyleName(styleName);
  }

  /**
   * Get the cell style name.
   *
   * @return cell style name.
   * @since 0.4
   */
  public String getCellStyleName() {
    return mCellElement.getStyleName();
  }

  /**
   * Set the cell value as a string, and set the value type to be string.
   *
   * @param str
   *            the value of string type. If input string is null, an empty
   *            string will be set.
   */
  public void setStringValue(String str) {
    if (str == null) {
      str = "";
    }
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.STRING);
    mCellElement.setOfficeStringValueAttribute(str);
    setDisplayTextContent(str, null);
  }

  // Note: if you want to change the cell
  // splitRepeatedCells must be called first in order to
  // 1. update parent row if the row is the repeated rows.
  // 2. update the cell itself if the cell is the column repeated cells.
  void splitRepeatedCells() {
    Table table = getTable();
    TableTableRowElement ownerRowElement = getTableRowElement();
    // 1.if the parent row is the repeated row
    // the repeated row has to be separated
    // after this the cell element and repeated index will be updated
    // according to the new parent row
    Row ownerRow = table.getRowInstance(ownerRowElement, mnRepeatedRowIndex);
    if (ownerRow.getRowsRepeatedNumber() > 1) {
      ownerRow.splitRepeatedRows();
      // update row element, new row element maybe created.
      ownerRowElement = ownerRow.maRowElement;
      mnRepeatedRowIndex = 0;
    }
    // 2.if the cell is the column repeated cell
    // this repeated cell has to be separated
    int repeateNum = getColumnsRepeatedNumber();
    if (repeateNum > 1) {
      // change this repeated cell to three parts: repeated cell before,
      // new single cell and repeated cell after.
      Map<TableTableCellElementBase, Vector<Cell>> cellRepository = table.mCellRepository;
      String tableNamespaceURI = OdfDocumentNamespace.TABLE.getUri();
      Vector<Cell> oldList = null;
      if (cellRepository.containsKey(mCellElement)) {
        oldList = cellRepository.remove(mCellElement);
      }
      int offetAfterCurrentCell = repeateNum - mnRepeatedColIndex - 1;
      TableTableCellElementBase currentCellElement = mCellElement;
      TableTableCellElementBase newBeforeCellElement = null;
      TableTableCellElementBase newAfterCellElement = null;
      if (mnRepeatedColIndex > 0) {
        newBeforeCellElement = (TableTableCellElementBase) mCellElement.cloneNode(true);
        if (mnRepeatedColIndex > 1) {
          newBeforeCellElement.setTableNumberColumnsRepeatedAttribute(mnRepeatedColIndex);
        } else {
          newBeforeCellElement.removeAttributeNS(tableNamespaceURI, "number-columns-repeated");
        }
        // insert new before repeated cell
        ownerRowElement.insertBefore(newBeforeCellElement, currentCellElement);
        // update cell cache
        if (oldList != null) {
          Vector<Cell> newBeforeList = new Vector<Cell>(mnRepeatedColIndex);
          for (int i = 0; i < mnRepeatedColIndex && i < oldList.size(); i++) {
            Cell beforeCell = oldList.get(i);
            if (beforeCell != null) {
              beforeCell.mCellElement = newBeforeCellElement;
              newBeforeList.add(i, beforeCell);
            }
          }
          cellRepository.put(newBeforeCellElement, newBeforeList);
        }
      }
      currentCellElement.removeAttributeNS(tableNamespaceURI, "number-columns-repeated");
      if (offetAfterCurrentCell > 0) {
        newAfterCellElement = (TableTableCellElementBase) currentCellElement.cloneNode(true);
        ownerRowElement.insertBefore(newAfterCellElement, currentCellElement);
        currentCellElement = newAfterCellElement;
        newAfterCellElement = (TableTableCellElementBase) currentCellElement.getNextSibling();
        if (offetAfterCurrentCell > 1) {
          newAfterCellElement.setTableNumberColumnsRepeatedAttribute(offetAfterCurrentCell);
        }
        // update cell cache
        if (oldList != null) {
          Vector<Cell> newAfterList = new Vector<Cell>(offetAfterCurrentCell);
          for (int i = mnRepeatedColIndex + 1; i < repeateNum && i < oldList.size(); i++) {
            Cell afterCell = oldList.get(i);
            if (afterCell != null) {
              afterCell.mCellElement = newAfterCellElement;
              afterCell.mnRepeatedColIndex = i - mnRepeatedColIndex - 1;
              newAfterList.add(afterCell.mnRepeatedColIndex, afterCell);
            }
          }
          cellRepository.put(newAfterCellElement, newAfterList);
        }
      }
      mnRepeatedColIndex = 0;
      mCellElement = currentCellElement;
      // update cell cache
      Vector<Cell> currentList = new Vector<Cell>(1);
      currentList.add(0, this);
      cellRepository.put(currentCellElement, currentList);
    }
  }

  /**
   * Get the cell value as a string.
   * <p>
   * If the cell type is not string, the display text will be returned.
   *
   * @return the string value of this cell, or the display text
   */
  public String getStringValue() {
    return getDisplayText();
  }

  /**
   * Get the cell value as {@link java.util.Calendar java.util.Calendar}.
   * <p>
   * Throw exception if the cell type is not "time".
   *
   * @return the Calendar value of cell
   * @throws IllegalArgumentException
   *             an IllegalArgumentException will be thrown if the cell type
   *             is not time.
   */
  public Calendar getTimeValue() {
    if (getTypeAttr() == OfficeValueTypeAttribute.Value.TIME) {
      String timeStr = mCellElement.getOfficeTimeValueAttribute();
                        if (timeStr == null) {
                            return null;
                        }
      Date date = parseString(timeStr, DEFAULT_TIME_FORMAT);
      Calendar calender = Calendar.getInstance();
      calender.setTime(date);
      calender.clear(Calendar.YEAR);
      calender.clear(Calendar.MONTH);
      calender.clear(Calendar.DAY_OF_MONTH);
      return calender;
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Set the cell value as a time and set the value type to be "time" too.
   *
   * @param time
   *            the value of {@link java.util.Calendar java.util.Calendar}
   *            type.
   * @throws IllegalArgumentException
   *             If input time is null, an IllegalArgumentException exception
   *             will be thrown.
   */
  public void setTimeValue(Calendar time) {
    if (time == null) {
      throw new IllegalArgumentException("time shouldn't be null.");
    }
    splitRepeatedCells();
    setTypeAttr(OfficeValueTypeAttribute.Value.TIME);
    SimpleDateFormat simpleFormat = new SimpleDateFormat(DEFAULT_TIME_FORMAT);
    String svalue = simpleFormat.format(time.getTime());
    mCellElement.setOfficeTimeValueAttribute(svalue);
    setDisplayTextContent(svalue, null);
  }

  private Date parseString(String value, String format) {
    SimpleDateFormat simpleFormat = new SimpleDateFormat(format);
    Date simpleDate = null;
    try {
      simpleDate = simpleFormat.parse(value);
    } catch (ParseException e) {
      Logger.getLogger(Cell.class.getName()).log(Level.SEVERE, e.getMessage(), e);
      return null;
    }
    return simpleDate;
  }

  /**
   * Get the background color of this cell.
   * <p>
   * If no background color is set, default background color "#FFFFFF" will be
   * returned.
   *
   * @return the background color of this cell
   */
  public Color getCellBackgroundColor() {
    return getStyleHandler().getBackgroundColor();
  }

  /**
   * Get the background color string of this cell.
   * <p>
   * If no background color is set, default background color "#FFFFFF" will be
   * returned.
   *
   * @return the background color of this cell
   *
   * @see #getCellBackgroundColor()
   * @deprecated As of Simple version 0.3, replaced by
   *             <code>getCellBackgroundColor()</code>
   */
  @Deprecated
  public String getCellBackgroundColorString() {
    String color = DEFAULT_BACKGROUND_COLOR;
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForRead();
    if (styleElement != null) {
      OdfStyleProperty bkColorProperty = OdfStyleProperty.get(OdfStylePropertiesSet.TableCellProperties, OdfName
          .newName(OdfDocumentNamespace.FO, "background-color"));
      String property = styleElement.getProperty(bkColorProperty);
      if (Color.isValid(property)) {
        color = property;
      }
    }
    return color;
  }

  /**
   * Set the background color of this cell.
   *
   * @param cellBackgroundColor
   *            the background color that need to set. If
   *            <code>cellBackgroundColor</code> is null, default background
   *            color <code>Color.WHITE</code> will be set.
   */
  public void setCellBackgroundColor(Color cellBackgroundColor) {
    getStyleHandler().setBackgroundColor(cellBackgroundColor);
  }

  /**
   * Set the background color of this cell using string. The string must be a
   * valid argument for constructing {@link org.odftoolkit.odfdom.type.Color
   * <code>org.odftoolkit.odfdom.type.Color</code>}.
   *
   * @param cellBackgroundColor
   *            the background color that need to set. If cellBackgroundColor
   *            is null, default background color #FFFFFF will be set.
   * @see org.odftoolkit.odfdom.type.Color
   * @see #setCellBackgroundColor(Color)
   * @deprecated As of Simple version 0.3, replaced by
   *             <code>setCellBackgroundColor(Color)</code>
   */
  @Deprecated
  public void setCellBackgroundColor(String cellBackgroundColor) {
    if (!Color.isValid(cellBackgroundColor)) {
      Logger.getLogger(Cell.class.getName()).log(Level.WARNING,
          "Parameter is invalid for datatype Color, default background color #FFFFFF will be set.");
      cellBackgroundColor = DEFAULT_BACKGROUND_COLOR;
    }
    splitRepeatedCells();
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForWrite();
    if (styleElement != null) {
      OdfStyleProperty bkColorProperty = OdfStyleProperty.get(OdfStylePropertiesSet.TableCellProperties, OdfName
          .newName(OdfDocumentNamespace.FO, "background-color"));
      styleElement.setProperty(bkColorProperty, cellBackgroundColor);
    }
  }

  /**
   * Get the column spanned number of this cell.
   *
   * @return the column spanned number
   */
  public int getColumnSpannedNumber() {
    if (mCellElement instanceof TableCoveredTableCellElement) {
      return 1;
    }
    Integer value = ((TableTableCellElement) mCellElement).getTableNumberColumnsSpannedAttribute();
    if (value != null) {
      return value.intValue();
    }
    return DEFAULT_COLUMN_SPANNED_NUMBER;
  }

  /**
   * Get the column repeated number of this cell.
   *
   * @return the column repeated number
   */
  int getColumnsRepeatedNumber() {
    Integer value = mCellElement.getTableNumberColumnsRepeatedAttribute();
    if (value != null) {
      return value.intValue();
    }
    return DEFAULT_COLUMNS_REPEATED_NUMBER;
  }

  /**
   * Get the row spanned number of this cell.
   *
   * @return the row spanned number
   */
  public int getRowSpannedNumber() {
    if (mCellElement instanceof TableCoveredTableCellElement) {
      return 1;
    }
    Integer value = ((TableTableCellElement) mCellElement).getTableNumberRowsSpannedAttribute();
    if (value != null) {
      return value.intValue();
    }
    return DEFAULT_ROW_SPANNED_NUMBER;
  }

  /**
   * Set the column spanned number.
   *
   * @param spannedNum
   *            the column spanned number to be set. If spannedNum is less
   *            than 1, default column spanned number 1 will be set.
   */
  void setColumnSpannedNumber(int spannedNum) {
    if (spannedNum < 1) {
      spannedNum = DEFAULT_COLUMN_SPANNED_NUMBER;
    }
    splitRepeatedCells();
    if (mCellElement instanceof TableTableCellElement) {
      ((TableTableCellElement) mCellElement).setTableNumberColumnsSpannedAttribute(new Integer(spannedNum));
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Set the column repeated number.
   *
   * @param repeatedNum
   *            the column repeated number that need to be set. If repeatedNum
   *            is less than 1, default columns repeated number 1 will be set.
   */
  void setColumnsRepeatedNumber(int repeatedNum) {
    if (repeatedNum < 1) {
      repeatedNum = DEFAULT_COLUMNS_REPEATED_NUMBER;
    }
    mCellElement.setTableNumberColumnsRepeatedAttribute(new Integer(repeatedNum));
  }

  /**
   * Set the row spanned number.
   *
   * @param spannedNum
   *            row spanned number that need to be set the row spanned number
   *            that need to be set. If spannedNum is less than 1, default row
   *            spanned number 1 will be set.
   */
  void setRowSpannedNumber(int spannedNum) {
    if (spannedNum < 1) {
      spannedNum = DEFAULT_ROW_SPANNED_NUMBER;
    }
    splitRepeatedCells();
    if (mCellElement instanceof TableTableCellElement) {
      ((TableTableCellElement) mCellElement).setTableNumberRowsSpannedAttribute(new Integer(spannedNum));
    } else {
      throw new IllegalArgumentException();
    }
  }

  /**
   * Judge if the ODF DOM element of this cell is the covered cell element.
   *
   * @return true if the Odf element is TableCoveredTableCellElement
   */
  boolean isCoveredElement() {
    if (mCellElement instanceof TableCoveredTableCellElement) {
      return true;
    }
    return false;
  }

  /**
   * Get the style name of this cell.
   *
   * @return the name of the style
   */
  public String getStyleName() {
    OdfStyleBase style = getStyleHandler().getStyleElementForRead();
    if (style == null) {
      return "";
    }
    if (style instanceof OdfStyle)
      return ((OdfStyle) style).getStyleNameAttribute();
    else
      return "";
  }

  /**
   * Set a formula to the cell.
   * <p>
   * Please note, the parameter <code>formula</code> will not be checked and
   * interpreted; the cell value will not be calculated. It's just simply set
   * as a formula attribute.
   *
   * @param formula
   *            the formula that need to be set.
   * @see org.odftoolkit.odfdom.dom.attribute.table.TableFormulaAttribute
   * @throws IllegalArgumentException
   *             if formula is null, an IllegalArgumentException will be
   *             thrown.
   */
  public void setFormula(String formula) {
    if (formula == null) {
      throw new IllegalArgumentException("formula shouldn't be null.");
    }
    splitRepeatedCells();
    mCellElement.setTableFormulaAttribute(formula);
  }

  /**
   * Get the formula string of the cell.
   *
   * @return the formula representation of the cell
   *         <p>
   *         If the cell does not contain a formula, null will be returned.
   *
   */
  public String getFormula() {
    return mCellElement.getTableFormulaAttribute();
  }

  // /**
  // * get the error value of the cell
  // * if the formula can not be calculated, an error will be set
  // * @return
  // * return 0 if the cell has no error
  // * return the error value of the cell if the formula result can not be
  // calculated
  // * such as divided by 0
  // */
  // public long getError()
  // {
  // return 0;
  // }
  /**
   * Set the currency symbol and overall format of a currency cell.
   * <p>
   * Please note the overall format includes the symbol character, for
   * example: $#,##0.00.
   * <p>
   * This function only works for currency.
   *
   * @param currencySymbol
   *            the currency symbol
   * @param format
   *            overall format
   * @throws IllegalArgumentException
   *             if input currencySymbol or format is null, an
   *             IllegalArgumentException will be thrown.
   */
  public void setCurrencyFormat(String currencySymbol, String format) {
    if (currencySymbol == null) {
      throw new IllegalArgumentException("currencySymbol shouldn't be null.");
    }
    if (format == null) {
      throw new IllegalArgumentException("format shouldn't be null.");
    }
    splitRepeatedCells();
    String type = mCellElement.getOfficeValueTypeAttribute();
    OfficeValueTypeAttribute.Value typeValue = null;
    msFormatString = format;
    if (type != null) {
      typeValue = OfficeValueTypeAttribute.Value.enumValueOf(type);
    }

    if (typeValue != OfficeValueTypeAttribute.Value.CURRENCY) {
      throw new IllegalArgumentException();
    }

    OdfNumberCurrencyStyle currencyStyle = new OdfNumberCurrencyStyle((OdfFileDom) mCellElement.getOwnerDocument(),
        currencySymbol, format, getUniqueCurrencyStyleName());
    mCellElement.getAutomaticStyles().appendChild(currencyStyle);
    setDataDisplayStyleName(currencyStyle.getStyleNameAttribute());
    Double value = getCurrencyValue();

    // set display text
    if (value != null) {
      setDisplayTextContent(formatCurrency(currencyStyle, value.doubleValue()), null);
    }
  }

  // This method doesn't handle style:map element.
  private String formatCurrency(OdfNumberCurrencyStyle currencyStyle, double value) {
    String valuestr = "";
    for (Node m : new DomNodeList(currencyStyle.getChildNodes())) {
      if (m instanceof NumberCurrencySymbolElement) {
        valuestr += m.getTextContent();
      } else if (m instanceof NumberNumberElement) {
        String numberformat = currencyStyle.getNumberFormat();
        valuestr += (new DecimalFormat(numberformat)).format(value);
      } else if (m instanceof NumberTextElement) {
        String textcontent = m.getTextContent();
        if (textcontent == null || textcontent.length() == 0) {
          textcontent = " ";
        }
        valuestr += textcontent;
      }
    }
    return valuestr;
  }

/**
   * Set the format string of the cell.
   * <p>
   * This function only works for float, date, time and percentage, otherwise an
   * {@link java.lang.IllegalArgumentException} will be thrown.
   * <p>
   * For value type float and percentage, the <code>formatStr</code> must follow the encoding
   * rule of {@link java.text.DecimalFormat <code>java.text.DecimalFormat</code>}.
   * For value type date and time, the <code>formatStr</code> must follow the encoding
   * rule of {@link java.text.SimpleDateFormat <code>java.text.SimpleDateFormat</code>}.
   * <p>
   * Refer to {@link org.odftoolkit.simple.table.Cell#setCurrencyFormat <code>setCurrencyFormat</code>} to set the format of currency.
   * <p>
   * If the cell value type is not set, the method will try to give it a value type, according
   * to common ordination. The adapt order is: percentage-> time-> date-> float.
   * <blockquote>
   * <table border=0 cellspacing=3 cellpadding=0 summary="Chart showing ValueType, Distinguish Symbol
   * and Distinguish Priority.">
   *     <tr bgcolor="#ccccff">
   *          <th align=left>ValueType
   *          <th align=left>Distinguish Symbol
   *          <th align=left>Distinguish Priority
   *     <tr valign=top>
   *          <td>percentage
   *          <td>%
   *          <td>1
   *     <tr valign=top>
   *          <td>time
   *          <td>H, k, m, s, S
   *          <td>2
   *     <tr valign=top>
   *          <td>date
   *          <td>y, M, w, W, D, d, F, E, K, h
   *          <td>3
   *     <tr valign=top>
   *          <td>float
   *          <td>#, 0
   *          <td>4
   * </table>
   * </blockquote>
   * The adapt result may be inaccurate, so you'd better set value type before call this method.
   * If adaptive failed, an {@link java.lang.UnsupportedOperationException} will be thrown.
   * <p>
   * @param formatStr  the cell need be formatted as this specified format string.
   * @throws IllegalArgumentException if <code>formatStr</code> is null or the cell value type is supported.
   * @throws UnsupportedOperationException if the adaptive failed, when cell value type is not set.
   * @see java.text.SimpleDateFormat
   * @see java.text.DecimalFormat
   */
  public void setFormatString(String formatStr) {
    if (formatStr == null) {
      throw new IllegalArgumentException("formatStr shouldn't be null.");
    }
    String type = getValueType();
    if (type == null) {
      if (formatStr.contains("%")) {
        setValueType("percentage");
      } else if (formatStr.contains("H") || formatStr.contains("k") || formatStr.contains("m")
          || formatStr.contains("s") || formatStr.contains("S")) {
        setValueType("time");
      } else if (formatStr.contains("y") || formatStr.contains("M") || formatStr.contains("w")
          || formatStr.contains("W") || formatStr.contains("D") || formatStr.contains("d")
          || formatStr.contains("F") || formatStr.contains("E") || formatStr.contains("K")
          || formatStr.contains("h")) {
        setValueType("date");
      } else if (formatStr.contains("#") || formatStr.contains("0")) {
        setValueType("float");
      } else {
        throw new UnsupportedOperationException("format string: " + formatStr
            + " can't be adapted to a possible value type.");
      }
      type = getValueType();
    }
    setCellFormatString(formatStr, type);
  }

  private void setCellFormatString(String formatStr, String type) {
    OfficeValueTypeAttribute.Value typeValue = null;
    msFormatString = formatStr;
    splitRepeatedCells();
    typeValue = OfficeValueTypeAttribute.Value.enumValueOf(type);
    if (typeValue == OfficeValueTypeAttribute.Value.FLOAT) {
      OdfNumberStyle numberStyle = new OdfNumberStyle((OdfFileDom) mCellElement.getOwnerDocument(), formatStr,
          getUniqueNumberStyleName());
      mCellElement.getAutomaticStyles().appendChild(numberStyle);
      setDataDisplayStyleName(numberStyle.getStyleNameAttribute());
      Double value = getDoubleValue();
      if (value != null) {
        setDisplayTextContent((new DecimalFormat(formatStr)).format(value.doubleValue()), null);
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.DATE) {
      OdfNumberDateStyle dateStyle = new OdfNumberDateStyle((OdfFileDom) mCellElement.getOwnerDocument(),
          formatStr, getUniqueDateStyleName(), null);
      mCellElement.getAutomaticStyles().appendChild(dateStyle);
      setDataDisplayStyleName(dateStyle.getStyleNameAttribute());
      String dateStr = mCellElement.getOfficeDateValueAttribute();
      if (dateStr != null) {
        Calendar date = getDateValue();
        setDisplayTextContent((new SimpleDateFormat(formatStr)).format(date.getTime()), null);
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.TIME) {
      OdfNumberTimeStyle timeStyle = new OdfNumberTimeStyle((OdfFileDom) mCellElement.getOwnerDocument(),
          formatStr, getUniqueDateStyleName());
      mCellElement.getAutomaticStyles().appendChild(timeStyle);
      setDataDisplayStyleName(timeStyle.getStyleNameAttribute());
      String timeStr = mCellElement.getOfficeTimeValueAttribute();
      if (timeStr != null) {
        Calendar time = getTimeValue();
        setDisplayTextContent((new SimpleDateFormat(formatStr)).format(time.getTime()), null);
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.PERCENTAGE) {
      OdfNumberPercentageStyle dateStyle = new OdfNumberPercentageStyle((OdfFileDom) mCellElement
          .getOwnerDocument(), formatStr, getUniquePercentageStyleName());
      mCellElement.getAutomaticStyles().appendChild(dateStyle);
      setDataDisplayStyleName(dateStyle.getStyleNameAttribute());
      Double value = getPercentageValue();
      if (value != null) {
        setDisplayTextContent((new DecimalFormat(formatStr)).format(value.doubleValue()), null);
      }
    } else {
      throw new IllegalArgumentException("This function doesn't support " + typeValue + " cell.");
    }
  }

  private void setDataDisplayStyleName(String name) {
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForWrite();
    if (styleElement != null) {
      styleElement.setOdfAttributeValue(OdfName.newName(OdfDocumentNamespace.STYLE, "data-style-name"), name);
    }
  }

  private String getDataDisplayStyleName() {
    String datadisplayStylename = null;
    OdfStyleBase styleElement = getStyleHandler().getStyleElementForRead();
    if (styleElement != null) {
      datadisplayStylename = styleElement.getOdfAttributeValue(OdfName.newName(OdfDocumentNamespace.STYLE,
          "data-style-name"));
    }

    return datadisplayStylename;
  }

  private String getUniqueNumberStyleName() {
    String unique_name;
    OdfOfficeAutomaticStyles styles = mCellElement.getAutomaticStyles();
    do {
      unique_name = String.format("n%06x", (int) (Math.random() * 0xffffff));
    } while (styles.getNumberStyle(unique_name) != null);
    return unique_name;
  }

  private String getUniqueDateStyleName() {
    String unique_name;
    OdfOfficeAutomaticStyles styles = mCellElement.getAutomaticStyles();
    do {
      unique_name = String.format("d%06x", (int) (Math.random() * 0xffffff));
    } while (styles.getDateStyle(unique_name) != null);
    return unique_name;
  }

  private String getUniquePercentageStyleName() {
    String unique_name;
    OdfOfficeAutomaticStyles styles = mCellElement.getAutomaticStyles();
    do {
      unique_name = String.format("p%06x", (int) (Math.random() * 0xffffff));
    } while (styles.getPercentageStyle(unique_name) != null);
    return unique_name;
  }

  // private String getUniqueCellStyleName() {
  // String unique_name;
  // OdfOfficeAutomaticStyles styles = mCellElement.getAutomaticStyles();
  // do {
  // unique_name = String.format("a%06x", (int) (Math.random() * 0xffffff));
  // } while (styles.getStyle(unique_name, OdfStyleFamily.TableCell) != null);
  // return unique_name;
  // }
  private String getUniqueCurrencyStyleName() {
    String unique_name;
    OdfOfficeAutomaticStyles styles = mCellElement.getAutomaticStyles();
    do {
      unique_name = String.format("c%06x", (int) (Math.random() * 0xffffff));
    } while (styles.getCurrencyStyle(unique_name) != null);
    return unique_name;
  }

  /**
   * Get the format string of the cell.
   *
   * @return the format string of the cell
   */
  public String getFormatString() {
    String type = mCellElement.getOfficeValueTypeAttribute();
    OfficeValueTypeAttribute.Value typeValue = null;
    if (type != null) {
      typeValue = OfficeValueTypeAttribute.Value.enumValueOf(type);
    }

    if (typeValue == OfficeValueTypeAttribute.Value.FLOAT) {
      String name = getDataDisplayStyleName();
      OdfNumberStyle style = mCellElement.getAutomaticStyles().getNumberStyle(name);
      if (style == null) {
        style = mDocument.getDocumentStyles().getNumberStyle(name);
      }
      if (style != null) {
        return style.getFormat();
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.DATE) {
      String name = getDataDisplayStyleName();
      OdfNumberDateStyle style = mCellElement.getAutomaticStyles().getDateStyle(name);
      if (style == null) {
        style = mDocument.getDocumentStyles().getDateStyle(name);
      }
      if (style != null) {
        return style.getFormat();
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.TIME) {
      String name = getDataDisplayStyleName();
      OdfNumberDateStyle style = mCellElement.getAutomaticStyles().getDateStyle(name);
      if (style == null) {
        style = mDocument.getDocumentStyles().getDateStyle(name);
      }
      if (style != null) {
        return style.getFormat();
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.CURRENCY) {
      String name = getCurrencyDisplayStyleName();
      OdfNumberCurrencyStyle dataStyle = mCellElement.getAutomaticStyles().getCurrencyStyle(name);
      if (dataStyle == null) {
        dataStyle = mDocument.getDocumentStyles().getCurrencyStyle(name);
      }
      if (dataStyle != null) {
        return dataStyle.getFormat();
      }
    } else if (typeValue == OfficeValueTypeAttribute.Value.PERCENTAGE) {
      String name = getDataDisplayStyleName();
      OdfNumberPercentageStyle style = mCellElement.getAutomaticStyles().getPercentageStyle(name);
      if (style == null) {
        style = mDocument.getDocumentStyles().getPercentageStyle(name);
      }
      if (style != null) {
        return style.getFormat();
      }
    }
    return null;
  }

  private String getCurrencyDisplayStyleName() {
    String name = getDataDisplayStyleName();
    OdfNumberCurrencyStyle dataStyle = mCellElement.getAutomaticStyles().getCurrencyStyle(name);
    if (dataStyle == null) {
      dataStyle = mDocument.getDocumentStyles().getCurrencyStyle(name);
    }

    if (dataStyle != null) {
      return dataStyle.getConditionStyleName(getCurrencyValue());
    }
    return null;
  }

  /**
   * Remove all the text content of cell.
   */
  public void removeTextContent() {
    splitRepeatedCells();
    // delete text:p child element
    Node node = mCellElement.getFirstChild();
    while (node != null) {
      Node nextNode = node.getNextSibling();
      if (node instanceof TextPElement || node instanceof TextHElement || node instanceof TextListElement) {
        mCellElement.removeChild(node);
      }
      node = nextNode;
    }
  }

  /**
   * Remove all the content of the cell.
   */
  public void removeContent() {
    splitRepeatedCells();
    Node node = mCellElement.getFirstChild();
    while (node != null) {
      Node nextNode = node.getNextSibling();
      mCellElement.removeChild(node);
      node = nextNode;
    }
  }

  /**
   * Append the content of another cell.
   *
   * @param fromCell
   *            another cell whose content will be appended to this cell
   */
  void appendContentFrom(Cell fromCell) {
    splitRepeatedCells();
    TableTableCellElementBase cell = fromCell.getOdfElement();
    Node node = cell.getFirstChild();
    while (node != null) {
      if (node instanceof OdfTextParagraph) {
        if (!TextExtractor.getText((OdfTextParagraph) node).equals("")) {
          mCellElement.appendChild(node.cloneNode(true));
        }
      } else {
        mCellElement.appendChild(node.cloneNode(true));
      }
      node = node.getNextSibling();
    }
  }

  /*****************************************
   * Moved from Table
   *
   *******************************************/
  /**
   * This method is invoked by insertCellBefore and insertRowBefore When it is
   * needed to clone a cell and the cell is a cover cell, for some instance,
   * we need to find the cell who covers this cell. So this method is to get
   * the cell who covers this cell
   */
  Cell getCoverCell() {
    int startRowi = getRowIndex();
    int startColumni = getColumnIndex();

    for (int i = startRowi; i >= 0; i--) {
      Row aRow = mOwnerTable.getRowByIndex(i);
      for (int j = startColumni; j >= 0; j--) {
        if (i == startRowi && j == startColumni) {
          continue;
        }
        Cell cell = aRow.getCellByIndex(j);
        if (cell.getOdfElement() instanceof TableTableCellElement) {
          TableTableCellElement cellEle = (TableTableCellElement) cell.getOdfElement();
          if ((cellEle.getTableNumberColumnsSpannedAttribute() + j > startColumni)
              && (cellEle.getTableNumberRowsSpannedAttribute() + i > startRowi)) {
            return mOwnerTable.getCellInstance(cellEle, 0, 0);
          }
        }
      }
    }
    return null;
  }

  /**
   * This method is invoked by getCoverCell. It's to get the cell in a same
   * row who covers this cell.
   *
   * @return the cell in a same row who covers this cell
   *         <p>
   *         Null if there is no cell who covers this cell
   */
  Cell getCoverCellInSameRow() {
    int startRowi = getRowIndex();
    int startColumni = getColumnIndex();

    for (int j = startColumni - 1; j >= 0; j--) {
      Cell cell = mOwnerTable.getCellByPosition(j, startRowi);
      if (cell.getOdfElement() instanceof TableCoveredTableCellElement) {
        continue;
      }

      int oldSpanN = cell.getColumnSpannedNumber();
      if (oldSpanN + j > startColumni) {
        // cell.setColumnSpannedNumber(oldSpanN-1);
        return cell;
      }
      return null;
    }
    return null;
  }

  /**
   * This method is invoked by getCoverCell
   */
  Cell getCoverCellInSameColumn() {
    int startRowi = getRowIndex();
    int startColumni = getColumnIndex();

    for (int i = startRowi - 1; i >= 0; i--) {
      Cell cell = mOwnerTable.getCellByPosition(startColumni, i);
      if (cell.getOdfElement() instanceof TableCoveredTableCellElement) {
        continue;
      }

      int oldSpanN = cell.getRowSpannedNumber();
      if (oldSpanN + i > startRowi) {
        // cell.setRowSpannedNumber(oldSpanN-1);
        return cell;
      }
      return null;
    }
    return null;
  }

  /**
   * Return the font definition for this cell.
   * <p>
   * Null will be returned if there is no explicit style definition, or even
   * default style definition, for this cell.
   *
   * @return the font definition null if there is no style definition for this
   *         cell
   *
   * @since 0.3
   */
  public Font getFont() {
    return getStyleHandler().getFont(Document.ScriptType.WESTERN);

  }

  /**
   * Set font style for this cell.
   *
   * @param font
   *            - the font
   *
   * @since 0.3
   */
  public void setFont(Font font) {
    getStyleHandler().setFont(font);
  }

  /**
   * Set the border style definitions for this cell.
   * <p>
   * This method will invoke
   * <code>CellStyleHandler.setBorders(Border border, SimpleCellBordersType bordersType).</code>
   *
   * @param bordersType
   *            - A predefined border type
   * @param border
   *            - border style description
   *
   * @see CellStyleHandler#setBorders(Border border,
   *      StyleTypeDefinitions.CellBordersType bordersType)
   * @since 0.3
   */
  public void setBorders(StyleTypeDefinitions.CellBordersType bordersType, Border border) {
    getStyleHandler().setBorders(border, bordersType);
  }

  /**
   * Return the border setting for a specific border.
   * <p>
   * This method will invoke
   * <code>CellStyleHandler.getBorder(SimpleCellBordersType type).</code>
   *
   * @param type
   *            - the border type which describes a single border
   * @return the border setting
   *
   * @see CellStyleHandler#getBorder(StyleTypeDefinitions.CellBordersType
   *      type)
   * @since 0.3.5
   */
  public Border getBorder(CellBordersType type) {
    return getStyleHandler().getBorder(type);
  }

  /**
   * Get the note text of this table cell. If there is no note on this cell,
   * <code>null</code> will be returned.
   * <p>
   * The note may contain text list, text paragraph and styles, but this
   * method extracts only text from them.
   *
   * @return the note text of this cell.
   */
  public String getNoteText() {
    String noteString = null;
    OfficeAnnotationElement annotation = OdfElement.findFirstChildNode(OfficeAnnotationElement.class, mCellElement);
    if (annotation != null) {
      noteString = "";
      Node n = annotation.getFirstChild();
      while (n != null) {
        Node m = n.getNextSibling();
        if (n instanceof TextPElement || n instanceof TextListElement) {
          noteString += TextExtractor.getText((OdfElement) n);
        }
        n = m;
      }
    }
    return noteString;
  }

  /**
   * Set note text for this table cell. This method creates a text paragraph
   * without style as note. The note text is text paragraph content.
   * <p>
   * Only simple text is supported to receive in this method, which is a sub
   * function of office annotation. So overwriting a note with text might
   * loose structure and styles.
   *
   * @param note
   *            note content.
   */
  public void setNoteText(String note) {
    splitRepeatedCells();
    OfficeAnnotationElement annotation = OdfElement.findFirstChildNode(OfficeAnnotationElement.class, mCellElement);
    if (annotation == null) {
      OdfFileDom dom = (OdfFileDom) mCellElement.getOwnerDocument();
      annotation = (OfficeAnnotationElement) OdfXMLFactory.newOdfElement(dom, OdfName.newName(
          OdfDocumentNamespace.OFFICE, "annotation"));
    }
    TextPElement noteElement = OdfElement.findFirstChildNode(TextPElement.class, annotation);
    if (noteElement == null) {
      noteElement = annotation.newTextPElement();
    }
    noteElement.setTextContent(note);
    DcCreatorElement dcCreatorElement = OdfElement.findFirstChildNode(DcCreatorElement.class, annotation);
    if (dcCreatorElement == null) {
      dcCreatorElement = annotation.newDcCreatorElement();
    }
    dcCreatorElement.setTextContent(System.getProperty("user.name"));
    String dcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date());
    DcDateElement dcDateElement = OdfElement.findFirstChildNode(DcDateElement.class, annotation);
    if (dcDateElement == null) {
      dcDateElement = annotation.newDcDateElement();
    }
    dcDateElement.setTextContent(dcDate);
    mCellElement.appendChild(annotation);
  }

  /**
   * Insert an Image from the specified uri to cell. Note: if there is any
   * other text content in this cell, it will be removed.
   *
   * @param imageUri
   *            The URI of the image that will be added to the cell, add image
   *            stream to the package, in the 'Pictures/' graphic directory
   *            with the same image file name as in the URI. If the imageURI
   *            is relative first the user.dir is taken to make it absolute.
   * @since 0.4.5
   */
  public Image setImage(URI imageUri) {
    if (imageUri == null)
      return null;

    splitRepeatedCells();
    Image newImage;
    try {
      NodeList cellPs = mCellElement.getElementsByTagNameNS(OdfDocumentNamespace.TEXT.getUri(), "p");
      if (cellPs != null && cellPs.getLength() > 0) {
        for (int i = 0; i < cellPs.getLength(); i++) {
          mCellElement.removeChild(cellPs.item(i));
        }
      }

      if (mOwnerTable.mIsSpreadsheet) {
        newImage = Image.newImage(this, imageUri);
      } else {
        OdfFileDom dom = (OdfFileDom) mCellElement.getOwnerDocument();
        TextPElement pElement = dom.newOdfElement(TextPElement.class);
        mCellElement.appendChild(pElement);
        newImage = Image.newImage(Paragraph.getInstanceof(pElement), imageUri);
      }
      if (imageUri != null) {
        FrameRectangle rect = newImage.getRectangle();
        double height = rect.getHeight();
        double width = rect.getWidth();
        long widthInMI = new Double(width / 100).longValue();
        Column column = getTableColumn();
        if (widthInMI > column.getWidth()) {
          column.setWidth(widthInMI);
        }

        long heightInMI = new Double(height / 100).longValue();
        Row row = getTableRow();
        if (heightInMI > row.getHeight()) {
          row.setHeight(heightInMI, false);
        }
        return newImage;
      }
    } catch (Exception ex) {
      Logger.getLogger(Cell.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
  }

  /**
   * Get the Image from the specified cell.
   *
   * @return If there is a image exist in this cell, An {@link java.awt.Image
   *         Image} will be returned.
   * @since 0.4.5
   * @deprecated
   */
  public BufferedImage getBufferedImage() {
    try {
      TextPElement pElement = OdfElement.findFirstChildNode(TextPElement.class, mCellElement);
      if (pElement != null) {
        DrawFrameElement drawFrame = OdfElement.findFirstChildNode(DrawFrameElement.class, pElement);
        if (drawFrame != null) {
          DrawImageElement imageElement = OdfElement.findFirstChildNode(DrawImageElement.class, drawFrame);
          if (imageElement != null) {
            String packagePath = imageElement.getXlinkHrefAttribute();
            OdfFileDom dom = (OdfFileDom) mCellElement.getOwnerDocument();
            OdfPackage mOdfPackage = dom.getDocument().getPackage();
            InputStream is = mOdfPackage.getInputStream(packagePath);
            BufferedImage image = ImageIO.read(is);
            return image;
          }
        }
      }
    } catch (Exception ex) {
      Logger.getLogger(Cell.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
  }

  /**
   * Get the image from the specified cell.
   *
   * @return If there is a image exist in this cell, an
   *         {@link org.odftoolkit.simple.draw.Image Image} will be returned.
   * @since 0.5.5
   */
  public Image getImage() {
    try {
      TextPElement pElement = OdfElement.findFirstChildNode(TextPElement.class, mCellElement);
      if (pElement != null) {
        DrawFrameElement drawFrame = OdfElement.findFirstChildNode(DrawFrameElement.class, pElement);
        if (drawFrame != null) {
          DrawImageElement imageElement = OdfElement.findFirstChildNode(DrawImageElement.class, drawFrame);
          return Image.getInstanceof(imageElement);
        }
      } else {
        DrawFrameElement drawFrame = OdfElement.findFirstChildNode(DrawFrameElement.class, mCellElement);
        if (drawFrame != null) {
          DrawImageElement imageElement = OdfElement.findFirstChildNode(DrawImageElement.class, drawFrame);
          return Image.getInstanceof(imageElement);
        }
      }
    } catch (Exception ex) {
      Logger.getLogger(Cell.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
  }
 
  /**
   * Return style handler for this cell
   *
   * @return the style handler
   *
   * @since 0.3
   */
  public CellStyleHandler getStyleHandler() {
    if (mStyleHandler == null)
      mStyleHandler = new CellStyleHandler(this);
    return mStyleHandler;
  }

  public OdfElement getListContainerElement() {
    return getListContainerImpl().getListContainerElement();
  }

  public org.odftoolkit.simple.text.list.List addList() {
    Document ownerDocument = getTable().getOwnerDocument();
    if (ownerDocument instanceof SpreadsheetDocument) {
      throw new UnsupportedOperationException(
          "Open Office and Symphony can't show a list in spreadsheet document cell.");
    } else {
      return getListContainerImpl().addList();
    }
  }

  public org.odftoolkit.simple.text.list.List addList(ListDecorator decorator) {
    Document ownerDocument = getTable().getOwnerDocument();
    if (ownerDocument instanceof SpreadsheetDocument) {
      throw new UnsupportedOperationException(
          "Open Office and Symphony can't show a list in spreadsheet document cell.");
    } else {
      return getListContainerImpl().addList(decorator);
    }
  }

  public void clearList() {
    getListContainerImpl().clearList();
  }

  public Iterator<org.odftoolkit.simple.text.list.List> getListIterator() {
    return getListContainerImpl().getListIterator();
  }

  public boolean removeList(org.odftoolkit.simple.text.list.List list) {
    return getListContainerImpl().removeList(list);
  }

  /**
   * Creates a new paragraph and append text
   *
   * @param text
   * @return the new paragraph
   * @throws Exception
   *             if the file DOM could not be created.
   * @since 0.5.5
   */
  public Paragraph addParagraph(String text) {
    Paragraph para = getParagraphContainerImpl().addParagraph(text);
    return para;
  }

  /**
   * Remove paragraph from this document
   *
   * @param para
   *            - the instance of paragraph
   * @return true if the paragraph is removed successfully, false if errors
   *         happen.
   *        
   * @since 0.5.5
   */
  public boolean removeParagraph(Paragraph para) {
    return getParagraphContainerImpl().removeParagraph(para);
  }
 
  /**
   * Get the ODF element which can have <text:p> as child element directly.
   *
   * @return - an ODF element which can have paragraph as child.
   *
   * @since 0.5.5
   */
  public OdfElement getParagraphContainerElement() {
    return getParagraphContainerImpl().getParagraphContainerElement();
  }
  /**
   * Return a paragraph with a given index.
   * <p>
   * An index of zero represents the first paragraph.
   * <p>
   * If empty paragraph is skipped, the empty paragraph won't be counted.
   *
   * @param index
   *            - the index started from 0.
   * @param isEmptyParagraphSkipped
   *            - whether the empty paragraph is skipped or not
   * @return the paragraph with a given index
   *
   * @since 0.5.5
   */
  public Paragraph getParagraphByIndex(int index, boolean isEmptyParagraphSkipped) {
    return getParagraphContainerImpl().getParagraphByIndex(index, isEmptyParagraphSkipped);
  }
 
  /**
   * Return a paragraph with a given index. The index is in reverse order.
   * <p>
   * An index of zero represents the last paragraph.
   * <p>
   * If empty paragraph is skipped, the empty paragraph won't be counted.
   *
   * @param reverseIndex
   *            - the index started from 0 in reverse order.
   * @param isEmptyParagraphSkipped
   *            - whether the empty paragraph is skipped or not
   * @return the paragraph with a given index
   *
   * @since 0.5.5
   */
  public Paragraph getParagraphByReverseIndex(int reverseIndex, boolean isEmptyParagraphSkipped) {
    return getParagraphContainerImpl().getParagraphByReverseIndex(reverseIndex, isEmptyParagraphSkipped);
  }
 
  /**
   * Return an Iterator of the paragraph in this container.
   *
   * @return an Iterator of the paragraph in this container
   *
   * @since 0.5.5
   */
  public Iterator<Paragraph> getParagraphIterator() {
    return getParagraphContainerImpl().getParagraphIterator();
  }

  public OdfElement getFrameContainerElement() {
    return mCellElement;
  }

  /**
   * Specifies the allowed values of this cell in a list. Any value out of
   * this list is invalid.
   * <p>
   * NOTE: Now, the validity rule does not take effect when a cell value
   * is updated by Simple ODF API yet.
   *
   * @param values
   *            the list of allowed values.
   * @since 0.6
   */
  public void setValidityList(List<String> values) {
    try {
      TableContentValidationElement validation = getContentValidationEle();
      validation.setTableAllowEmptyCellAttribute(true);
      String split = "";
      StringBuilder listStr = new StringBuilder("");
      for (String value : values) {
        listStr.append(split);
        listStr.append("\"");
        listStr.append(value);
        listStr.append("\"");
        split = ";";
      }
      validation.setTableConditionAttribute("cell-content-is-in-list(" + listStr + ")");
      String tableName = getTableElement().getTableNameAttribute();
      validation.setTableBaseCellAddressAttribute(tableName + "." + getCellAddress());
      validation.setTableDisplayListAttribute("unsorted");
    } catch (Exception e) {
      Logger.getLogger(Cell.class.getName()).log(Level.SEVERE, null, e);
    }
  }

  /**
   * Sets the title and the text of the tip, which will then be displayed if
   * the cell is selected.
   *
   * @param title
   *            the title of the tip.
   * @param text
   *            the text of the tip.
   * @since 0.6
   */
  public void setInputHelpMessage(String title, String text) {
    try {
      TableContentValidationElement validationElement = getContentValidationEle();
      TableHelpMessageElement helpMessageElement = OdfElement.findFirstChildNode(TableHelpMessageElement.class,
          validationElement);
      if (helpMessageElement != null) {
        validationElement.removeChild(helpMessageElement);
      }
      helpMessageElement = validationElement.newTableHelpMessageElement();
      helpMessageElement.setTableTitleAttribute(title);
      helpMessageElement.setTableDisplayAttribute(true);
      helpMessageElement.newTextPElement().setTextContent(text);
    } catch (Exception e) {
      Logger.getLogger(Cell.class.getName()).log(Level.SEVERE, null, e);
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////// private methods ///////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
  private TableContentValidationElement getContentValidationEle() throws Exception {
    Document ownerDocument = getOwnerDocument();
    OdfElement contentRootElement = ownerDocument.getContentRoot();
    TableContentValidationsElement validations = OdfElement.findFirstChildNode(
        TableContentValidationsElement.class, contentRootElement);
    if (validations == null) {
      validations = (TableContentValidationsElement) OdfXMLFactory.newOdfElement(ownerDocument
          .getContentDom(), OdfName.newName(OdfDocumentNamespace.TABLE, "content-validations"));
      contentRootElement.insertBefore(validations, contentRootElement.getFirstChild());
    }
    String validationName = getOdfElement().getTableContentValidationNameAttribute();
    TableContentValidationElement validationElement = null;
    if (validationName != null) {
      Node child = validations.getFirstChild();
      while (child != null) {
        TableContentValidationElement contentValidationElementRef = (TableContentValidationElement) child;
        if (validationName.equals(contentValidationElementRef.getTableNameAttribute())) {
          validationElement = contentValidationElementRef;
          break;
        }
      }
    } else {
      String valName = "val" + String.format("d%06x", (int) (Math.random() * 0xffffff));
      validationElement = validations.newTableContentValidationElement(valName);
      getOdfElement().setTableContentValidationNameAttribute(valName);
    }
    return validationElement;
  }

  private String getCellAddress() {
    char[] digits = { '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
        'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
    int i = getColumnIndex() + 1;
    char[] buf = new char[32];
    int charPos = 32;
    do {
      buf[--charPos] = digits[i % 26];
      i /= 26;
    } while (i != 0);
    String cs = new String(buf, charPos, (32 - charPos));
    return cs + (getRowIndex() + 1);
  }
 
  private ListContainerImpl getListContainerImpl() {
    if (listContainerImpl == null) {
      listContainerImpl = new ListContainerImpl();
    }
    return listContainerImpl;
  }

  private class ListContainerImpl extends AbstractListContainer {

    public OdfElement getListContainerElement() {
      return mCellElement;
    }
  }
 
  private class ParagraphContainerImpl extends AbstractParagraphContainer {
    public OdfElement getParagraphContainerElement() {
      return mCellElement;
    }
  }

  private ParagraphContainerImpl getParagraphContainerImpl() {
    if (paragraphContainerImpl == null)
      paragraphContainerImpl = new ParagraphContainerImpl();
    return paragraphContainerImpl;
  }
 
  //column can fit the width to the text, if column.isOptimalWidth() is true.
  //since 0.5.5
  private void optimizeCellSize(String content){
    JTextField txtField = new JTextField();
    // map font to awt font
    Font font = getFont();
    String fontFamilyName = font.getFamilyName();
    if (fontFamilyName == null) {
      fontFamilyName = "Times New Roman";
    }
    int fontStyleNum = java.awt.Font.PLAIN;
    FontStyle fontStyle = font.getFontStyle();
    if (fontStyle != null) {
      switch (fontStyle) {
      case BOLD:
        fontStyleNum = java.awt.Font.BOLD;
        break;
      case REGULAR:
        fontStyleNum = java.awt.Font.PLAIN;
        break;
      case ITALIC:
        fontStyleNum = java.awt.Font.ITALIC;
        break;
      case BOLDITALIC:
        fontStyleNum = java.awt.Font.BOLD | java.awt.Font.ITALIC;
        break;
      default:
        fontStyleNum = java.awt.Font.PLAIN;
      }
    }
    double fontSize = font.getSize();
    if (fontSize < 0.0001) {
      fontSize = 10;
    }
    txtField.setFont(new java.awt.Font(fontFamilyName, fontStyleNum, (int)fontSize));
    FontMetrics fm = txtField.getFontMetrics(txtField.getFont());
    // content width in pixels.
    int widthPixels = fm.stringWidth(content);
    // content height in pixels.
    // int heightPixels = fm.getHeight();
    OdfStyleBase properties = getStyleHandler().getStyleElementForRead();
    double millimeterPadding = 0.0;
    if(properties!=null){
      String padding = properties.getProperty(StyleTableCellPropertiesElement.Padding);
      if(padding!=null){
        millimeterPadding = Length.parseDouble(padding, Unit.MILLIMETER);
      }
    }
    // convert width pixels to mm
    double columnWidth = widthPixels / 2.83464;
    columnWidth += millimeterPadding * 2;
    Column column = getTableColumn();
    if (column.isOptimalWidth()) {
      double width = column.getWidth();
      if (width < columnWidth) {
        column.setWidth(columnWidth);
      }
    }
  }
}
TOP

Related Classes of org.odftoolkit.simple.table.Cell

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.