All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlCell Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.xml.msofficexml.xlsx.spreadsheetml;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import com.hfg.datetime.DateUtil;
import com.hfg.util.BooleanUtil;
import com.hfg.util.StringUtil;
import com.hfg.util.collection.CollectionUtil;
import com.hfg.xml.XMLAttribute;
import com.hfg.xml.XMLTag;
import com.hfg.xml.XMLUtil;
import com.hfg.xml.msofficexml.xlsx.CellRange;
import com.hfg.xml.msofficexml.xlsx.CellRef;
import com.hfg.xml.msofficexml.xlsx.spreadsheetml.style.SsmlCellFormat;


//------------------------------------------------------------------------------
/**
 Represents an Office Open XML worksheet data cell (<ssml:c>) tag.

 @author J. Alex Taylor, hairyfatguy.com
 */
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------

public class SsmlCell extends SsmlXMLTag
{
   private SsmlWorksheet  mParentWorksheet;
   private SsmlCellFormat mCellFormat;

   private CellRange      mMergedCellRange;

   private Object  mValue;
   private String  mStringValue;
   private XMLTag  mValueTag;
   private XMLTag  mFormulaTag;



   private static final LocalDate REFERENCE_DATE;

   static
   {
      Calendar cal = new GregorianCalendar();
      cal.set(1900, Calendar.JANUARY, 1);
      REFERENCE_DATE = cal.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
   }

   //###########################################################################
   // CONSTRUCTORS
   //###########################################################################

   //---------------------------------------------------------------------------
   public SsmlCell(SsmlWorksheet inParentWorksheet)
   {
      super(SsmlXML.CELL, inParentWorksheet.getParentDoc());
      mParentWorksheet = inParentWorksheet;
   }

   //---------------------------------------------------------------------------
   public SsmlCell(SsmlWorksheet inParentWorksheet, XMLTag inXMLTag)
   {
      this(inParentWorksheet);
      inXMLTag.verifyTagName(SsmlXML.CELL);

      if (CollectionUtil.hasValues(inXMLTag.getAttributes()))
      {
         for (XMLAttribute attr : inXMLTag.getAttributes())
         {
            setAttribute(attr);
         }
      }

      // Copy subtags over to this tag
      if (CollectionUtil.hasValues(inXMLTag.getSubtags()))
      {
         for (XMLTag subtag : (List) (Object) inXMLTag.getSubtags())
         {
            addSubtag(subtag);
         }
      }
   }

   //###########################################################################
   // PUBLIC METHODS
   //###########################################################################

   //---------------------------------------------------------------------------
   public String toString()
   {
      Object value = getValue();
      return (value != null ? value.toString() : null);
   }

   //---------------------------------------------------------------------------
   public SsmlCellFormat getFormat()
   {
      if (null == mCellFormat)
      {
         // Check if it has been added via setAttribute()...
         if (hasAttribute(SsmlXML.STYLE_IDX_ATT))
         {
            int cellFormatId = Integer.parseInt(getAttributeValue(SsmlXML.STYLE_IDX_ATT));
            mCellFormat = getParentDoc().getStylesPart().getCellFormats().get(cellFormatId);
         }
         else
         {
            setFormat(new SsmlCellFormat(getParentDoc()));
         }
      }

      return mCellFormat;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setFormat(SsmlCellFormat inValue)
   {
      mCellFormat = inValue;

      if (inValue != null)
      {
         setAttribute(SsmlXML.STYLE_IDX_ATT, inValue.getIndex());
      }
      else
      {
         removeAttribute(SsmlXML.STYLE_IDX_ATT.getLocalName());
      }

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setRef(CellRef inValue)
   {
      if (inValue != null)
      {
         setAttribute(SsmlXML.REF_ATT, inValue);
      }
      else
      {
         removeAttribute(SsmlXML.REF_ATT.getLocalName());
      }

      return this;
   }

   //---------------------------------------------------------------------------
   public CellRef getRef()
   {
      CellRef value = null;

      String stringValue = getAttributeValue(SsmlXML.REF_ATT);
      if (StringUtil.isSet(stringValue))
      {
         value = new CellRef(stringValue);
      }

      return value;
   }

   //---------------------------------------------------------------------------
   public SsmlCell mergeDown(int inValue)
   {
      CellRef cellRef = getRef();
      if (null == cellRef)
      {
         throw new RuntimeException("A CellRef must be specified for the cell before it can be merged!");
      }

      int endColIndex = cellRef.getColIndex();

      if (mMergedCellRange != null)
      {
         mParentWorksheet.removeMergeCell(mMergedCellRange);
         endColIndex = mMergedCellRange.getEndCell().getColIndex();
      }

      CellRef refForEndingCell = new CellRef().setColIndex(endColIndex).setRowIndex(cellRef.getRowIndex() + inValue);

      mMergedCellRange = new CellRange(cellRef, refForEndingCell);
      mParentWorksheet.mergeCells(mMergedCellRange);

      // Ensure that the rest of the cells being merge have been created
      ensureMergedCellsExist(mMergedCellRange);

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell mergeRight(int inValue)
   {
      CellRef cellRef = getRef();
      if (null == cellRef)
      {
         throw new RuntimeException("A CellRef must be specified for the cell before it can be merged!");
      }

      int endRowIndex = cellRef.getRowIndex();

      if (mMergedCellRange != null)
      {
         mParentWorksheet.removeMergeCell(mMergedCellRange);
         endRowIndex = mMergedCellRange.getEndCell().getRowIndex();
      }

      CellRef refForEndingCell = new CellRef().setColIndex(cellRef.getColIndex() + inValue).setRowIndex(endRowIndex);

      mMergedCellRange = new CellRange(cellRef, refForEndingCell);
      mParentWorksheet.mergeCells(mMergedCellRange);

      // Ensure that the rest of the cells being merge have been created
      ensureMergedCellsExist(mMergedCellRange);

      return this;
   }

   //---------------------------------------------------------------------------
   public void addComment(XlsxComment inComment)
   {
      XlsxComment comment = inComment.clone().setCellRef(getRef());
      mParentWorksheet.getCommentsPart().addComment(comment);
   }

   //---------------------------------------------------------------------------
   @Override
   public String getContent()
   {
      return mStringValue;
   }

   //---------------------------------------------------------------------------
   @Override
   public SsmlCell setContent(CharSequence inContent)
   {
      setValue(inContent);

      return this;
   }

   //---------------------------------------------------------------------------
   @Override
   public SsmlCell addContent(CharSequence inContent)
   {
      return setValue((mStringValue != null ? mStringValue : "") + inContent.toString());
   }

   //---------------------------------------------------------------------------
   public SsmlCellType getCellType()
   {
      String attrValueString = getAttributeValue(SsmlXML.CELL_DATA_TYPE_ATT);

      return StringUtil.isSet(attrValueString) ? SsmlCellType.valueOf(attrValueString) : null;
   }
/*
   //---------------------------------------------------------------------------
   public SsmlCell setValue(String inValue)
   {
      setAttribute(SsmlXML.CELL_DATA_TYPE_ATT, DataType.inlineStr);

      XMLTag inlineStrTag = new XMLTag(SsmlXML.INLINE_STRING);
      addSubtag(inlineStrTag);

      XMLTag textTag = new XMLTag(SsmlXML.TEXT);
      textTag.setContent(inValue);
      inlineStrTag.addSubtag(textTag);

      return this;
   }
*/

   //---------------------------------------------------------------------------
   public SsmlCell setValue(Object inValue)
   {
      if (inValue != null)
      {
         if (inValue instanceof Integer)
         {
            setValue(((Integer)inValue).intValue());
         }
         else if (inValue instanceof Long)
         {
            setValue(((Long)inValue).longValue());
         }
         else if (inValue instanceof Float)
         {
            setValue(((Float)inValue).floatValue());
         }
         else if (inValue instanceof Double)
         {
            setValue(((Double)inValue).doubleValue());
         }
         else if (inValue instanceof Date)
         {
            // Store dates w/o a type and with a number value that is the number of days after Jan 1, 1900
            LocalDate inputDate = ((Date)inValue).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

            long days = ChronoUnit.DAYS.between(REFERENCE_DATE, inputDate);

            // For some obscure reason, Excel files built on a mac use 1904 as the reference date...
            if (getParentDoc().getWorkbook().getProperties().get1904BasedDates())
            {
               days -= (365 * 4);
            }
            else
            {
               // Not exactly sure why this is necessary but it is
               days += 2;
            }
            
            getValueTag().setContent(days + "");

            // Remember the string value
            mStringValue = DateUtil.getISO_8601_Date((Date)inValue);

            // Make sure that the cell is formatted as a date
            if (null == mCellFormat)
            {
               //
               setFormat(getParentDoc().getStylesPart().getDefaultDateCellFormat());
            }
         }
         else
         {
            setValue(inValue.toString());
         }
      }

      mValue = inValue;

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setValue(SsmlTextRun inValue)
   {
      setAttribute(SsmlXML.CELL_DATA_TYPE_ATT, SsmlCellType.s);

      int index = getParentDoc().getSharedStringsPart().defineString(inValue);

      getValueTag().setContent(index + "");

      // Remember the string value
      mStringValue = inValue.toXML();
      mValue = mStringValue;

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setTextRuns(List inValue)
   {
      setAttribute(SsmlXML.CELL_DATA_TYPE_ATT, SsmlCellType.s);

      int index = getParentDoc().getSharedStringsPart().defineString(inValue);

      getValueTag().setContent(index + "");

      // Remember the string value
      StringBuilder xml = new StringBuilder();
      for (SsmlTextRun run : inValue)
      {
         xml.append(run.toXML());
      }

      mStringValue = xml.toString();

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setValue(String inValue)
   {
      if (inValue != null)
      {
         setAttribute(SsmlXML.CELL_DATA_TYPE_ATT, SsmlCellType.s);

         int index = getParentDoc().getSharedStringsPart().defineString(inValue);

         getValueTag().setContent(index + "");

         // Remember the string value
         mStringValue = inValue;
         mValue = inValue;
      }
      else
      {
         removeAttribute(SsmlXML.CELL_DATA_TYPE_ATT);
         if (mValueTag != null)
         {
            removeSubtag(mValueTag);
            mValueTag = null;
            mValue = null;
            mStringValue = null;
         }
      }

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setFormula(String inValue)
   {
      // Clear any value
      removeAttribute(SsmlXML.CELL_DATA_TYPE_ATT);
      if (mValueTag != null)
      {
         removeSubtag(mValueTag);
         mValueTag = null;
         mValue = null;
         mStringValue = null;
      }

      // Set the formula
      getFormulaTag().setContent(inValue);

      return this;
   }

   //---------------------------------------------------------------------------
   public SsmlCell setValue(int inValue)
   {
      return setPrimitiveNumberValue(inValue + "");
   }

   //---------------------------------------------------------------------------
   public SsmlCell setValue(long inValue)
   {
      return setPrimitiveNumberValue(inValue + "");
   }

   //---------------------------------------------------------------------------
   public SsmlCell setValue(float inValue)
   {
      return setPrimitiveNumberValue(inValue + "");
   }

   //---------------------------------------------------------------------------
   public SsmlCell setValue(double inValue)
   {
      return setPrimitiveNumberValue(inValue + "");
   }


   //---------------------------------------------------------------------------
   public Object getValue()
   {
      if (null == mValue)
      {
         XMLTag valueTag = getValueTag();

         Object value = null;

         if (StringUtil.isSet(valueTag.getContent()))
         {
            SsmlCellType cellType = getCellType();
            if (null == cellType)
            {
               cellType = SsmlCellType.n;
            }


            switch (cellType)
            {
               case s: // shared string
                  value = XMLUtil.unescapeContent(getParentDoc().getSharedStringsPart().getString(Integer.parseInt(valueTag.getContent())));
                  break;
               case str: // string stored in the value tag
                  value = valueTag.getUnescapedContent();
                  break;
               case n: // number
                  try
                  {
                     Double doubleValue = Double.parseDouble(valueTag.getContent());
                     if (doubleValue.intValue() == doubleValue)
                     {
                        value = doubleValue.intValue();
                     }
                     else
                     {
                        value = doubleValue;
                     }
                  }
                  catch (Exception e)
                  {
                     value = valueTag.getUnescapedContent();
                  }

                  // Is it a date in disguise?
                  if (getFormat() != null
                      && value instanceof Integer
                      && getFormat().getNumberFormat() != null
                      && StringUtil.isSet(getFormat().getNumberFormat().getFormatCode())
                      && getFormat().getNumberFormat().getFormatCode().toLowerCase().indexOf("y") >= 0)
                  {
                     // Dates are stored as a number value that is the number of days after Jan 1, 1900
                     value = getDateFromDays((Integer) value);
                  }

                  break;
               case b:
                  value = BooleanUtil.valueOf(valueTag.getUnescapedContent());
                  break;
               default:
                  System.err.println("Support for cell type " + cellType + " not yet added! Returning null.");
                  // TODO
            }

            mValue = value;
         }
      }

      return mValue;
   }

   //---------------------------------------------------------------------------
   private Date getDateFromDays(int inDays)
   {
      Date date = null;
      try
      {
         // For some obscure reason, Excel files built on a mac use 1904 as the reference date...
         if (getParentDoc().getWorkbook().getProperties().get1904BasedDates())
         {
            inDays += (365 * 4);
         }
         else
         {
            // Not exactly sure why this is necessary but it is
            inDays -= 2;
         }

         LocalDate localDate = REFERENCE_DATE.plus(inDays, ChronoUnit.DAYS);

         date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
      }
      catch (Exception e)
      {
         // Igore
      }

      return date;
   }

   //---------------------------------------------------------------------------
   private XMLTag getValueTag()
   {
      if (null == mValueTag)
      {
         // Check if it has been added via addSubtag()...
         mValueTag = getOptionalSubtagByName(SsmlXML.VALUE);
         if (null == mValueTag)
         {
            mValueTag = new XMLTag(SsmlXML.VALUE);
            addSubtag(mValueTag);
         }
      }

      return mValueTag;
   }

   //---------------------------------------------------------------------------
   private XMLTag getFormulaTag()
   {
      if (null == mFormulaTag)
      {
         // Check if it has been added via addSubtag()...
         mFormulaTag = getOptionalSubtagByName(SsmlXML.CELL_FORMULA);
         if (null == mFormulaTag)
         {
            mFormulaTag = new XMLTag(SsmlXML.CELL_FORMULA);
            addSubtag(mFormulaTag);
         }
      }

      return mFormulaTag;
   }

   //---------------------------------------------------------------------------
   private SsmlCell setPrimitiveNumberValue(String inStringValue)
   {
      setAttribute(SsmlXML.CELL_DATA_TYPE_ATT, SsmlCellType.n);

      getValueTag().setContent(inStringValue);

      // Remember the string value
      mStringValue = inStringValue;

      return this;
   }

   //---------------------------------------------------------------------------
   private void ensureMergedCellsExist(CellRange inCellRange)
   {
      SsmlSheetData sheetData = (SsmlSheetData) getParentNode().getParentNode();

      int startingRowIndex = inCellRange.getBeginCell().getRowIndex();
      int endingRowIndex = inCellRange.getEndCell().getRowIndex();

      for (int i = startingRowIndex; i <= endingRowIndex; i++)
      {
         SsmlRow row = sheetData.getRowByRowIndex(i);
         if (null == row)
         {
            row = sheetData.addRow(i);
         }

         for (int colIndx = inCellRange.getBeginCell().getColIndex(); colIndx <= inCellRange.getEndCell().getColIndex(); colIndx++)
         {
            SsmlCell cell = row.getCellByColIndex(colIndx);
            if (null == cell)
            {
               row.addCellByColIndex(colIndx);
            }
         }
      }
   }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy